A huge number of coding interview problems involve dealing with Permutations and Combinations of a given set of elements. This pattern describes an efficient backtracking approach to handle all these problems.
function backtrack(startIndex) {
if (shouldEnd()) {
handleResult();
return;
}
for (let i = startIndex; i < choices.length; i++) {
doChoice(i);
used[i] = true;
backtrack(i + 1);
revertChoice(i);
used[i] = false;
}
}
These questions are all related to choices, we can think about the process as a decision tree (check explanation in 78_subsets), every step we have some choices to choose, we go downwards first to choose one choice each step, after reaching the leaf (all choices are chosen at one branch), then we go upward again to choose other choices.
The key difference between the subset/permutation/combination problems is the loop variable i
above, take [1, 2, 3]
as input array:
subset
: the final result is[ [], [1], [1, 2], [1, 2, 3], [2], [2, 3], [3] ]
,i
should start withstartIndex
because once 1 is processed (starting from 1), 1 won't be appeared again when we process 2, and every time we hitbacktrack
the result should be pushed to the final array. In addition, noused
is required becausei
starts fromstartIndex
.permutation
: the final result is[ [1, 2, 3], [1, 3, 2], [2, 1, 3]. [2, 3, 1], [3, 1, 2], [3, 2, 1] ]
,i
should always start from index 0, cause once 1 is processed, it can still be appeared when we process 2.used
is required becausei
always starts from 0, we need to know ifi
is already used or not. In addition, result is only needed to be handled when it contains 3 numbers in it.combination
: the final result is[ [1, 2], [2, 3], [1, 3] ]
if we are trying to find the combination of 2. This is pretty similar assubsets
, the only difference is we need to handle result when it already contains 2 numbers in it.