# DFS backtracking classic sort combination

Backtracking has many different scenarios. Backtracking is actually DFS + pruning. It's almost enumeration, and then either finds that the branch that meets the conditions, or the current branch can no longer meet the conditions, pruning and giving up.

Let's write about the classical problem of sorting and combining.

subject brief introduction candidates
Whether there are duplicate elements
Element allowed or not
Reuse
With start
39. Combination Sum A combination whose sum is k Element distinct Allow multiple uses Yes
40. Combination Sum II A combination whose sum is k Elements can have duplicates There are several limits Yes
78. Subsets Find all subsets Element distinct What's the use Yes
90. Subsets II Find all subsets Elements can have duplicates There are several limits Yes
46. Permutations Find all permutations Element distinct -- (full use) nothing
47. Permutations II Find all permutations Elements can have duplicates -- (full use) nothing
77. Combinations (class 78) All sizeK subsets Element distinct Use k Yes
• Combination: start is required to limit the current layer to be selected from only one range (not the whole set), because some of them have been used. The combination problem is considered from the perspective of "whether to use the current resource". resource1 can be used or not. After the decision, resource2 can be used or not When waiting for the current layer, such as resource M, the previous M has been considered (and a decision has been made), so the current layer only considers the resources from M to the last part (that is, starting from start).
• Sort: start is not required. Because the sorting problem is considered from the perspective of "what are the legal candidate s for the current position". Candidates (i.e. resources) are sorted according to resources. The combination problem must be in order from the perspective of "whether to use the current resources". Therefore, start can be used to mark. Here, only a whole array of used [] can be used to record which resources have been used.

## 39. Combination Sum

Input: candidates = [2,3,6,7], target = 7,
A solution set is:
[
,
[2,2,3]
] candidates Chinese elements can be used countless times

The elements in this question candidates = [2,3,6,7] are allowed to be used indefinitely. If all combinations are found, there are infinite many. Fortunately, there is also a limit with target=7.

Because the candidates element is allowed to be used indefinitely, when you transfer start to the next level, you transfer i instead of i+1, and the next level still processes the current bit. Isn't that the one who never jumps next? That's right. 2, 2, 2 It's one of the branch es, until the total exceeds target, which does not meet the requirements, and is pruned.

If the candidates element is distinct, there is no need to sort. Sorting exists in the case of "candidates may have duplicate elements". Ensure that two elements with the same value are next to each other. We don't care about the value size relationship of the element itself.

```class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> lst = new ArrayList<>();
genCombination(lst, new ArrayList<Integer>(), candidates, target, 0);
return lst;
}

private void genCombination(List<List<Integer>> lst, List<Integer> path, int[] candidates, int remain, int start) {
if (remain == 0) {//good terminal
lst.add(new ArrayList<>(path));//Note: new a new ArrayList
return;
} else if (remain < 0) {//bad terminal
return;
}
for (int i = start; i < candidates.length; i++) {
genCombination(lst, path, candidates, remain-candidates[i], i);
path.remove(path.size()-1);
}
}
}
```

## 40. Combination Sum II

Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
] difference: candidates Each element in can only be used once

• 39: candidates element is allowed to be used indefinitely. i will be passed when i pass start next level.
• 40: candidates element is only allowed to be used once, and i+1 is passed when start is passed down.

There may be duplicate elements in candidates, so you need to sort [1,1,2,5,6,7,10] before backtracking to ensure that the elements with the same value are next to each other. Next to that, we're going to think "do we need to skip repeating elements?". Note that not all elements with the same value are skipped, [1,1,2 ]When processing the second 1, you cannot skip it, otherwise [1, 1, 6] will be missed.

• The "skip repetition" here refers to that if there is a repetition in the following number, such as [1,1,2,5,6,6,7,10], the second 6 will be skipped and will not be processed (same level horizontal - skip).
• And "1 of the previous layer and 1 of the current layer" is not skipped.

Use I > Start & & candidates [i] = = candidates [I-1] to define the skip condition, and exclude i = = start, that is, the first one in this layer (no skip).

```class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
List<List<Integer>> lst = new ArrayList<>();
List<Integer> path = new ArrayList<>();
getCombination(lst, path, candidates, target, 0);
return lst;
}

private void getCombination(List<List<Integer>> lst, List<Integer> path, int[] candidates, int remain, int start) {
if (remain == 0) {//good terminal
return;
} else if (remain < 0) {//bad terminal
return;
}
for (int i = start; i < candidates.length; i++) {
if (i > start && candidates[i] == candidates[i-1]) {continue;}
getCombination(lst, path, candidates, remain-candidates[i], i+1);
path.remove(path.size()-1);
}
}
}
```

## 78. Subsets

Input: nums = [1,2,3]， Input element distinct
Output:
[
,
,
,
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

39,40 combination sum takes "and reach target" as the success condition of a branch. 78,90 subset s have no identification standard, and all branches are required. In this case, it must be required that "the element cannot be used indefinitely", otherwise it will not be finished.

78 elements distinct, 90 allow to have duplicate elements, but they can be used at most when there are several values. Therefore, from the perspective of "whether to use the current resource", this one can't be used anymore after being used. Pass i+1 to the next level.

78 element distinct, there is no need for sorting, and there is no problem of skipping (skipping only needs to be considered if there are duplicate elements and sorted).

```class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
getSubset(list, new ArrayList<Integer>(), nums, 0);
return list;
}

private void getSubset(List<List<Integer>> list, List<Integer> path, int[] nums, int start) {
for (int i = start; i < nums.length; i++) {
getSubset(list, path, nums, i+1);
path.remove(path.size()-1);
}
}
}
```

## 90. Subsets II

Input: [1,2,2] Different: Input There may be duplicate elements
Output:
[
,
,
[1,2,2],
[2,2],
[1,2],
[]
]

Almost the same as 78, except for 78 element distinct, 90 allows duplicate elements. You need to sort, and you need to think about skipping. The skip method is the same as 40. Combination Sum II.

```class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> lst = new ArrayList<>();
Arrays.sort(nums);
getSubset(lst, new ArrayList<Integer>(), nums, 0);
return lst;
}

private void getSubset(List<List<Integer>> lst, List<Integer> path, int[] nums, int start) {
for (int i = start; i < nums.length; i++) {
if (i > start && nums[i] == nums[i-1]) {continue;}
getSubset(lst, path, nums, i+1);
path.remove(path.size()-1);
}
}
}
```

## 46. Permutations

Input: [1,2,3]，Array element distinct
Output:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

The above four are combinations. This is an arrangement. Because all elements need to be used, the order of using elements and input is not necessarily the same, so start cannot be used to limit the optional range.

How do you know which elements have been used? path.contains(nums[i]) as the check used condition. Because the values of each element are different, it's OK to check whether it has appeared directly in the ArrayList.

Note here that the check used condition is different from the skip condition. See explanation 47 below.

```class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> ret = new ArrayList<>();
backtracking(ret, new ArrayList<>(), nums);
return ret;
}

private void backtracking(List<List<Integer>> list, List<Integer> path, int[] nums) {
if (path.size() == nums.length) {
return;
}
for (int i = 0; i < nums.length; i++) {
if (path.contains(nums[i])) {continue;}
backtracking(list, path, nums);
path.remove(path.size()-1);
}
}
}
```

## 47. Permutations II

Input: [1,1,2] difference: input Array may have duplicate
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

• 46: "check if used" condition: path.contains(nums[i]). Because the values of each element are different, it's OK to check whether it has appeared directly in the ArrayList.
• 47: "check whether used" condition: used[i] == true. Because there may be elements with the same value, a separate array used [] is needed to record whether each element has been used.

The check used condition is different from the skip condition.
47 there is also a "skip" condition: similar to 40. Combination Sum II, 90. Subsets II.
i > 0 && nums[i] == nums[i-1] && !used[i-1]
The differences between 40 and 90 are as follows:

• 40,90: i = = start do not skip, that is, if the first element of each layer is the same as the element of the previous layer, do not skip. [1,1,2,5,6,7,10], that is, when processing the call of [1,1], the second 1 is not skipped.
• 47: used[i-1]= = true do not skip, that is, if the former element of the same two elements has been used, the latter will not skip.

In fact, the two situations are essentially the same. Both are: if the former is not used, the latter will skip (because everything is processed in the former). If the former is used, the latter cannot skip, and both  and [1,1] need to be considered. (40,90 because of sequential processing, i= =start is the case of "the former has been used, then the latter cannot be skipped").

```class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> lst = new ArrayList<>();
Arrays.sort(nums);
boolean[] used = new boolean[nums.length];
getPermutation(lst, new ArrayList<Integer>(), nums, used);
return lst;
}

private void getPermutation(List<List<Integer>> lst, List<Integer> path, int[] nums, boolean[] used) {
if (path.size() == nums.length) {
return;
}
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i-1] && !used[i-1]) {continue;}//Skip condition
if (used[i] == true) {continue;}//Check if it has been used
used[i] = true;
getPermutation(lst, path, nums, used);
used[i] = false;
path.remove(path.size()-1);
}
}
}
```

## 77. Combinations

Input: n = 4, k = 2
Output:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

Almost the same as 78. Subsets, except that only path with length of 2 is considered successful (only added to the result list). No code here.

Posted by gnawz on Thu, 04 Jun 2020 19:52:37 -0700