Permutations
Given an array nums of distinct integers, return all possible permutations (orderings). For [1,2,3] there are 3! = 6.
Intuition
Build each ordering slot by slot. For every position, try each number that hasn't been used yet, recurse to fill the next slot, then undo the choice and try the next number.
When order matters and every element must appear, you're permuting, not combining — so each level of the recursion scans the whole array rather than starting from an index. That's the structural difference from combination-sum: combinations use a start cursor to enforce order, permutations use a used array because there is no order to enforce.
Approach
Each permutation fixes a number per slot
A permutation is an arrangement that uses every element exactly once. So think of it as filling slots left to right: for slot 0 choose any number, for slot 1 any of the remaining, and so on. The branching factor shrinks by one each level until a full ordering is complete.
Track which numbers are used
Carry a used flag set (or boolean list). At each level, loop over all numbers and skip the ones already on the path. Pick an unused number, mark it used, append it, and recurse. This guarantees no value repeats within a single permutation.
Record at the leaf, then backtrack
When the path length equals the input length, every slot is filled — record a copy of the path. After each recursive call, pop the number and clear its used flag so the next iteration can try it elsewhere. That choose → explore → un-choose loop is the heart of backtracking. There are n! leaves.
Solution & live demo
Common pitfalls
Using a start index like a combination problem
def backtrack(start, path):
for i in range(start, len(nums)):
...def backtrack(path):
for i in range(len(nums)):
if used[i]: continue
...A start cursor only ever moves forward, so [2, 1] can never be built after [1, 2] — you'd generate combinations and return a single permutation. Permutations must be free to reach back to earlier elements, which is why membership is tracked by a flag rather than a position.
Clearing used[i] but not popping the path
path.append(nums[i]) backtrack(path) used[i] = False
path.append(nums[i]) backtrack(path) path.pop() used[i] = False
The two pieces of state must unwind together. Releasing the element for reuse while leaving it in path lets it appear twice in the same permutation and pushes path past len(nums). Undo every mutation you made, in reverse order.
Edge cases
One permutation, the list itself.
Returns [[]] — one permutation, the empty ordering.
With duplicates you'd get repeated permutations; Permutations II adds a sort-and-skip to dedupe.