Generate All Permutations

Say you have three friends and three chairs in a row. You want to write down every seating order. Should Alex sit first, or Riya? Then who sits next? Listing all those orders by hand gets messy fast. That is exactly the problem permutations solve.

A permutation is one possible ordering of all the items in a set. Take the set [1, 2, 3]. The orderings are [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1]. So there are six. Our job here is to make the computer produce all of them. No guessing, and nothing missed.

🎯 What We Are Trying to Do

We start with a list like [1, 2, 3]. We want every arrangement that uses each number exactly once.

The trick we use is called backtracking. Backtracking means we build an answer one step at a time. When one path is finished, we step back and try the next choice. Think of trying keys on a keyring. You try one key, it does not fit, so you go back and try the next.

The idea is simple. We fill the arrangement one position at a time. At each position we try every number that we have not used yet. After placing a number, we move on to the next position. When the arrangement is full, we record it. Then we undo the last choice and try a different number.

That β€œundo and try again” part is the heart of backtracking.

πŸͺ‘ The Three Chairs Picture

Let us walk through [1, 2, 3] slowly. We have three empty slots to fill.

For the first slot we can pick 1, 2, or 3. Say we pick 1. Now 1 is used. For the second slot we can pick 2 or 3. Say we pick 2. Now only 3 is left, so the third slot must be 3. That gives us [1, 2, 3].

Now we go back. We undo 3, then undo 2, and try 3 in the second slot instead. That gives [1, 3, 2]. We keep doing this until the first slot has tried 1, then 2, then 3.

This tree shows the choices when we start with 1.

start: empty

pick 1

pick 1,2

pick 1,3

1,2,3 done

1,3,2 done

The same branching happens when we start with 2 and when we start with 3. So in total we get six leaves, which are our six permutations.

πŸ”– How We Track What Is Used

We need a way to know which numbers are already sitting in the arrangement. We use a used array. It is a list of true/false flags, one for each number.

The flags do a few small jobs. Before we pick a number, we check its flag. If it is already true, we skip it. When we pick a number, we set its flag to true. When we undo that number, we set its flag back to false.

This stops the same number from showing up twice in one arrangement. Without it, we would get bad orderings like [1, 1, 1].

Tip

The undo step is easy to forget. Every time you set a flag to true and add a number, you must later set the flag back to false and remove the number. If you skip the undo, the next branch starts with wrong leftovers.

πŸͺœ Steps to Generate All Permutations

Now we turn the idea into code. We use a recursive helper that fills one position at a time.

  1. Keep a current list for the arrangement we are building, and a used array for the flags.
  2. If current has the same length as the input, we have a full arrangement. Record a copy of it and return.
  3. Otherwise, loop over every index in the input.
  4. If that index is already used, skip it.
  5. Mark it used, add its value to current.
  6. Call the helper again to fill the next position.
  7. After the call returns, undo: remove the value from current and mark the index unused.
  8. When the loop ends, all choices for this position have been tried.

That step 7 undo is what lets us reuse the same number on a different branch.

πŸ’» The Code

Each version builds a current list, uses a used array of flags, and prints every full arrangement. We run it on [1, 2, 3].

permutations.py
# current holds the arrangement so far, used marks taken numbers
def permute(nums, current, used, result):
if len(current) == len(nums): # arrangement is full
result.append(current[:]) # store a copy
return
for i in range(len(nums)):
if used[i]: # skip numbers already placed
continue
used[i] = True # mark used
current.append(nums[i])
permute(nums, current, used, result) # fill next slot
current.pop() # undo the choice (backtrack)
used[i] = False
def all_permutations(nums):
result = []
permute(nums, [], [False] * len(nums), result)
return result
for p in all_permutations([1, 2, 3]):
print(p)

The output of the above code will be:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

Note

Notice the order matters here. We add a number, recurse, then remove it. The recurse call must sit between the add and the remove. If you remove too early or too late, the printed arrangements come out wrong.

⏱️ How Slow Is This?

Permutations grow very fast. For a list of n items, the number of orderings is n factorial, written n!. That means 1 * 2 * 3 * ... * n.

See how quickly it climbs.

Items (n) Permutations (n!)
3 6
5 120
10 3,628,800

So with just 10 items you already have over three million orderings. There is no way around this. If you must list every ordering, you must produce that many.

Caution

The time cost is O(n! * n). The n! part is the number of arrangements. The extra n is the work to copy or print each full arrangement. Because of this, permutation code is only practical for small inputs. Do not run it on large lists.

Here is the cost in one place.

Measure Cost Why
Time O(n! * n) n! arrangements, each takes n work to build and print
Space (recursion) O(n) the call stack and the current list go n deep

⚠️ Common Mistakes

A few slip-ups trip up most students. Watch for these.

  • Forgetting the undo step. You must remove the number from current and set its flag back to false after recursing.
  • Storing the same list instead of a copy. In Python and JavaScript you must store current[:] or [...current], not current itself. Otherwise every saved result points to the same list, which keeps changing.
  • Checking the wrong flag, or never checking used at all. Skip this check and the same number repeats inside one arrangement.
  • Recording the arrangement at the wrong time. Record only when current is full, not on every step.

🧩 What You’ve Learned

βœ… A permutation is one ordering of all items, and a set of n items has n! orderings.

βœ… Backtracking builds each arrangement one position at a time, then undoes a choice to try the next.

βœ… A used array of flags stops a number from appearing twice in the same arrangement.

βœ… You always record a copy of the arrangement, never the live list.

βœ… The cost is O(n! * n), so this only works for small inputs.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    How many permutations does a set of 4 items have?

    Why: It is 4! which is 1 * 2 * 3 * 4 = 24.

  2. 2

    What is the purpose of the used array in this algorithm?

    Why: The used flags tell us which numbers are already in the current arrangement so we skip them.

  3. 3

    What happens if you forget the undo step (setting the flag back to false and removing the value)?

    Why: Without the undo, the flags and current list keep wrong leftovers, so other branches break.

  4. 4

    Why is the time complexity O(n! * n) and not just O(n!)?

    Why: There are n! full arrangements, and building or printing each one costs n steps.

πŸš€ What’s Next?

Share & Connect