回溯算法-【全排列】【组合总和】【n皇后】【子集】

回溯算法

 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。即从一条路往前走,能进则进,不能进则退回来,换一条路再试。

如何使用回溯算法

 回溯我认为也就是一种递归,有以下四个参数,当然不一定是我所举例的类型,要看题目而定
  (1)一个全局变量集合保存所有满足条件的答案,举例:List<List> res
  (2)一个集合保存一个满足条件的答案,举例:List tmpList
核心:根据各个题情况变换

1
2
3
4
5
6
7
8
for (int i = start; i <= n; i++) {
//add进去的值根据题意变换
tmplist.add(i);
//递归,这里根据题意变换回溯,这仅仅是个例子
backtrack( k - 1, n, i+1,tmplist, result);
//将这个集合清空,方便下一个满足条件的答案
tmplist.remove(tmplist.size()-1);
}

leetcode_【46】全排列 I

https://leetcode-cn.com/problems/permutations/

题目描述:
 给定一个没有重复数字的序列,返回其所有可能的全排列。
输入:
  [1,2,3]
输出:
  [
   [1,2,3],
   [1,3,2],
   [2,1,3],
   [2,3,1],
   [3,1,2],
   [3,2,1]
   ]

解题思路:

  回溯算法:

  • 将第j个数字与第 j , j +1 , j + 2 ,…, len(nums) - 1个数字分别交换,得到 len(nums) - j 种情况;
  • 在每种情况下递归,将第j+1处数字与第j+1,j+2,…,len(nums) - 1处数字分别交换;
    • 每个递归跳出后,要将交换过的元素还原,这样才能实现第一条所说的内容。
      • 直到j == len(nums) - 1,代表此种交换组合下已经交换完毕,记录答案。

代码

第一版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.util.ArrayList;
import java.util.List;
//执行用时 :41 ms, 在所有 Java 提交中击败了5.01%的用户
//内存消耗 :44.4 MB, 在所有 Java 提交中击败了5.03%的用户
public class Solution {
public static void main(String[] args) {
int [] arr = {1,2,1};
permute(arr);
}
public static List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
backTrace(nums,0,new ArrayList<>(),res);
return res;
}

private static void backTrace(int[] nums, int start,ArrayList<Integer> tempList, List<List<Integer>> res) {
//start为左边界值,要跟右边界的数进行交换
if(start == nums.length){
if(!res.contains(tempList)){
res.add(new ArrayList<>(tempList));
}
}
for(int i = start;i<nums.length;i++){
//将第j个数字与第 j , j +1 , j + 2 ,..., len(nums) - 1个数字分别交换
swap(nums,start,i);
tempList.add(nums[start]);
backTrace(nums,start+1,tempList,res);
tempList.remove(tempList.size()-1);
// 每个递归跳出后,要将交换过的元素还原
swap(nums,start,i);
}
}
//交换
private static void swap(int[] nums, int start, int i) {
int temp = nums[start];
nums[start] = nums[i];
nums[i] = temp;
}
}

第二版:leetcode官网解答

这里有一个回溯函数,使用第一个整数的索引作为参数 backtrack(first)。

  • 如果第一个整数有索引 n,意味着当前排列已完成。
  • 遍历索引 first 到索引 n - 1 的所有整数。
    • 在排列中放置第 i 个整数, 即 swap(nums[first], nums[i]).
    • 继续生成从第 i 个整数开始的所有排列: backtrack(first + 1).
    • 现在回溯,即通过 swap(nums[first], nums[i]) 还原.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 //执行用时 :3 ms, 在所有 Java 提交中击败了87.64%的用户
//内存消耗 :38.5 MB, 在所有 Java 提交中击败了67.89%的用户
class Solution {
public void backtrack(int n,ArrayList<Integer> nums, List<List<Integer>> output, int first) {
//如果第一个整数有索引 n,意味着当前排列已完成。
if (first == n)
output.add(new ArrayList<Integer>(nums));
// 遍历索引 first 到索引 n - 1 的所有整数。
for (int i = first; i < n; i++) {
//在排列中放置第 i 个整数
Collections.swap(nums, first, i);
// 继续生成从第 i 个整数开始的所有排列
backtrack(n, nums, output, first + 1);
//回溯
Collections.swap(nums, first, i);
}
}

public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> output = new LinkedList();

// convert nums into list since the output is a list of lists
ArrayList<Integer> nums_lst = new ArrayList<Integer>();
for (int num : nums)
nums_lst.add(num);

int n = nums.length;
backtrack(n, nums_lst, output, 0);
return output;
}
}

leetcode_【47】全排列 II

https://leetcode-cn.com/problems/permutations-ii/

题目描述:
 给定一个可能包含重复数字的序列,返回其所有可能的不重复全排列。
输入:
  [1,1,2]
输出:
  [
   [1,1,2],
   [1,2,1],
   [2,1,1],
   ]

代码

第一版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.ArrayList;
import java.util.List;
//执行用时 :742 ms, 在所有 Java 提交中击败了8.80%的用户
//内存消耗 :45.7 MB, 在所有 Java 提交中击败了44.99%的用户
public class Solution{
public static List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
backTrace(nums,0,new ArrayList<>(),res);
return res;
}

private static void backTrace(int[] nums, int start,ArrayList<Integer> tempList, List<List<Integer>> res) {
//start为边界值
if(start == nums.length){
//加了这一句
if(!res.contains(tempList)){
res.add(new ArrayList<>(tempList));
}
}
for(int i = start;i<nums.length;i++){
swap(nums,start,i);
tempList.add(nums[start]);
backTrace(nums,start+1,tempList,res);
tempList.remove(tempList.size()-1);
swap(nums,start,i);
}
}

private static void swap(int[] nums, int start, int i) {
int temp = nums[start];
nums[start] = nums[i];
nums[i] = temp;
}
}

优化的第二版:leetcode官网解答
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//执行用时 :6 ms, 在所有 Java 提交中击败了63.63%的用户
//内存消耗 :42.8 MB, 在所有 Java 提交中击败了76.28%的用户
public class Solution {

private List<List<Integer>> res = new ArrayList<>();
private boolean[] used;

private void findPermuteUnique(int[] nums, int depth, Stack<Integer> stack) {
if (depth == nums.length) {
res.add(new ArrayList<>(stack));
return;
}
for (int i = 0; i < nums.length; i++) {
if (!used[i]) {
// 修改 2:因为排序以后重复的数一定不会出现在开始,故 i > 0
// 和之前的数相等,并且之前的数还未使用过,只有出现这种情况,才会出现相同分支
// 这种情况跳过即可
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
used[i] = true;
stack.add(nums[i]);
findPermuteUnique(nums, depth + 1, stack);
stack.pop();
used[i] = false;
}
}
}

public List<List<Integer>> permuteUnique(int[] nums) {
int len = nums.length;
if (len == 0) {
return res;
}
// 修改 1:首先排序,之后才有可能发现重复分支
Arrays.sort(nums);
used = new boolean[len];
findPermuteUnique(nums, 0, new Stack<>());
return res;
}

public static void main(String[] args) {
int[] nums = {1, 1, 2};
Solution solution = new Solution();
List<List<Integer>> permuteUnique = solution.permuteUnique(nums);
System.out.println(permuteUnique);
}
}

leetcode_【77】组合

https://leetcode-cn.com/problems/combinations/

 给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例:
输入:
  n = 4, k = 2
输出:
   [
   [2,4],
   [3,4],
   [2,3],
   [1,2],
   [1,3],
   [1,4],
   ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
> //执行用时 :40 ms, 在所有 Java 提交中击败了52.77%的用户
> //内存消耗 :52.5 MB, 在所有 Java 提交中击败了26.41%的用户
> public class Solution {
> public static List<List<Integer>> combine(int n, int k) {
> List<List<Integer>> res = new ArrayList<>();
> backtrack(k,n,1,new ArrayList(),res);
> return res;
> }
> public static void backtrack(int k,int n,int start,List<Integer> tmplist,List<List<Integer>> result){
> if(k == 0){
> result.add(new ArrayList<>(tmplist));
> }else {
> for (int i = start; i <= n; i++) {
> tmplist.add(i);
> //递归,这里回溯 从 i+1 ~ n 中 k-1 个数的组合,直到k == 0就可将这次结果存到res里面
> backtrack( k - 1, n, i+1,tmplist, result);
> tmplist.remove(tmplist.size()-1);
> }
> }
> }
> }
>

leetcode_【39】组合总和 I

   https://leetcode-cn.com/problems/combination-sum/

 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以>使数字和为 target 的组合。
 candidates 中的数字可以无限制重复被选取。
说明:
  (1)所有数字(包括 target)都是正整数。
 (2)解集不能包含重复的组合。
示例 1:
输入:

   candidates = [2,3,6,7], target = 7,
所求解集为:
   [
   [7],
   [2,2,3]
   ]
示例 2:
输入:

  candidates = [2,3,5], target = 8,
所求解集为:
  [
  [2,2,2,2],
   [2,3,3],
   [3,5]
  ]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//执行用时 :7 ms, 在所有 Java 提交中击败了81.11%的用户
//内存消耗 :39.1 MB, 在所有 Java 提交中击败了87.66%的用户
class Solution {
public static List<List<Integer>> combinationSum(int[] candidates, int target) {
//存放结果
List<List<Integer>> res = new ArrayList<>();
//排序
Arrays.sort(candidates);
//从第一个数开始递归
calculate(candidates,target,0,new ArrayList<>(),res);

return res;
}
public static void calculate(int[] candidates,int target,int start,ArrayList<Integer> tmpList,List<List<Integer>> result) {
//回溯
if (target < 0) {
return;
}
//存入结果集
else if (target == 0) {
result.add(new ArrayList<>(tmpList));
return;
} else {
for (int i = start; i < candidates.length && target >= candidates[i]; i++) {
//加入
tmpList.add(candidates[i]);
//递归,candidates 中的数字可以使用无数次,故start还是从 i 开始
calculate( candidates, target - candidates[i], i, tmpList,result);
//清空所得到的一次结果的list
tmpList.remove(tmpList.size() - 1);
}
}
}

leetcode_【40】组合总和 II

   https://leetcode-cn.com/problems/combination-sum-ii/

  给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以只能使用一次。
说明:
  (1)所有数字(包括 target)都是正整数。
 (2)解集不能包含重复的组合。
输入:
  candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
   [
   [1, 7],
   [1, 2, 5],
   [2, 6],
   [1, 1, 6]
   ]
示例 2:
输入:

   candidates = [2,5,2,1,2], target = 5,
所求解集为:
   [
  [1,2,2],
   [5]
   ]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  //执行用时 :30 ms, 在所有 Java 提交中击败了22.59%的用户
//内存消耗 :45 MB, 在所有 Java 提交中击败了27.34%的用户
public class Solution {
public static List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
//排序
Arrays.sort(candidates);
//从下标为0,目标为target的开始回溯
backTrace(candidates,target,0,new ArrayList<>(),res);
return res;
}

private static void backTrace(int[] candidates, int target,int start, ArrayList<Integer> tmpList, List<List<Integer>> res) {
if(target<0)
return;
//
else if(target == 0){
//解集不能包含重复的组合。
Collections.sort(tmpList);
if(!res.contains(tmpList)){
res.add(new ArrayList<>(tmpList));
}
}else {
for (int i = start; i < candidates.length && target >= candidates[i]; i++) {
tmpList.add(candidates[i]);
//回溯,因为candidates 中的数字可以只能使用一次,所以这里start变成了i+1
backTrace(candidates, target - candidates[i], i + 1, tmpList, res);
tmpList.remove(tmpList.size() - 1);
}
}
}

leetcode_【216】组合总和III

https://leetcode-cn.com/problems/combination-sum-iii/submissions/

  找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
 所有数字都是正整数。
  解集不能包含重复的组合。
示例 1:
输入:

   k = 3, n = 7
输出:
   [[1,2,4]]
示例 2:
输入:

  k = 3, n = 9
输出:
   [[1,2,6], [1,3,5], [2,3,4]]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 //执行用时 :5 ms, 在所有 Java 提交中击败了8.96%的用户
//内存消耗 :34.1 MB, 在所有 Java 提交中击败了28.88%的用户
class Solution {
public static List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> res = new ArrayList<>();
backTrace(k,n,1,new ArrayList<>(),res);
return res;
}

private static void backTrace(int k, int target,int start, ArrayList<Integer> tmpList, List<List<Integer>> res) {
if(0 == k && target == 0){
Collections.sort(tmpList);
if(!res.contains(tmpList))
res.add(new ArrayList<>(tmpList));
}else{
// 组合数中只包含1-9的数,故i<=9
for(int i = start;i <= 9 ;i++){
tmpList.add(i);
backTrace(k-1,target - i,i+1,tmpList,res);
tmpList.remove(tmpList.size()-1);
}
}
}
}

leetcode_【377】组合总和 IV

 给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。
示例:
   nums = [1, 2, 3]
  target = 4
所有可能的组合为:
   (1, 1, 1, 1)
   (1, 1, 2)
   (1, 2, 1)
   (1, 3)
   (2, 1, 1)
   (2, 2)
   (3, 1)
请注意,顺序不同的序列被视作不同的组合。
因此输出为 7。

代码

回溯超出内存限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   public static int combinationSum4(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
backTrace(nums,target,0,new ArrayList<>(),res);
return res.size();
}

private static void backTrace(int[] nums, int target, int start, ArrayList<Integer> tmpList, List<List<Integer>> res) {
if(target < 0){
return;
}else if(target == 0){
res.add(new ArrayList<>(tmpList));
}else{
for(int i = 0;i<nums.length && target >= nums[i];i++){
tmpList.add(nums[i]);
backTrace(nums,target - nums[i],i,tmpList,res);
tmpList.remove(tmpList.size()-1);
}
}
}

动态规划:dp[i] 代表组成i 能有多少种组合数,即dp[1] = 2代表 和为1的组合个数为2

  • 初始化
    • dp[0] = 1;即组成和为0的组合数为1,即都nums里面的数都不取
  • 状态转移方程:

    • dp[i]=dp[ i - nums[0] ]+dp[ i - nums[1] ]+dp[ i - nums[2] ] + ...

      举个例子 : 比如nums=[1,3,4],target=7;dp[7]=dp[6]+dp[4]+dp[3]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//执行用时 :5 ms, 在所有 Java 提交中击败了68.54%的用户
//内存消耗 :34.3 MB, 在所有 Java 提交中击败了39.00%的用户
public static int combinationSum6(int[] nums, int target) {
int dp[] = new int[target + 1];
//初始化
dp[0] = 1;
for(int i = 1;i<=target;i++){
for(int j = 0;j<nums.length;j++){
if(i - nums[j] >= 0 ){
dp[i] += dp[i-nums[j]];
}
}
}
return dp[target];
}

leetcode_【51】N皇后 I

https://leetcode-cn.com/problems/n-queens/

  n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
  给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
  每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入:

  4
输出:
    [
    [“.Q..”, // 解法 1
    “…Q”,
    “Q…”,
    “..Q.” ],
    [ “..Q.”, // 解法 2
    “Q…”,
    “…Q”,
    “.Q..”]
    ]
解释: 4 皇后问题存在两个不同的解法。

代码

  • 回溯函数 backtrack(row = 0).
    • 从第一个 row = 0 开始.
    • 循环列并且试图在每个 column 中放置皇后.
      • 如果方格 (row, column) 不在攻击范围内
        • (row, column) 方格上放置皇后
        • 排除对应行,列和两个对角线的位置。
        • If 所有的行被考虑过,row == NunOfQueen
          • 意味着我们找到了一个解
        • else
          • 继续考虑接下来的皇后放置 backtrack(row + 1).
        • 回溯:将在 (row, column) 方格的皇后移除.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package leetcode.Arrays.回溯;

import java.lang.reflect.Array;
import java.util.*;
//执行用时 :9 ms, 在所有 Java 提交中击败了53.89%的用户
//内存消耗 :39.3 MB, 在所有 Java 提交中击败了84.75%的用户
public class Main_51 {
public static List<List<String>> solveNQueens(int n) {
//用于存储行
List<Integer> col = new ArrayList<>();
//用于存储正对角线
List<Integer> z_diagonal = new ArrayList<>();
//用于存储负对角线
List<Integer> f_diagonal = new ArrayList<>();
//存储结果
List<List<String>> res = new ArrayList<>();
//从第一个row = 0开始
backtrack(0, n, res, new ArrayList<String>(), col, z_diagonal, f_diagonal);
return res;
}
public static void backtrack(int row, int NumOfQueen, List<List<String>> res, ArrayList<String> tmplist, List<Integer> col, List<Integer> z_diagonal, List<Integer> f_diagonal) {
//到达了最后一行
if (row == NumOfQueen) {
res.add(new ArrayList<>(tmplist));
return;
}
//从第0列开始遍历
for (int column = 0; column < NumOfQueen; column++) {
//如果不在攻击范围内(不在同一行或者同一列 && 负对角线和相等 && 正对角线差相等)
if (!col.contains(column) && !f_diagonal.contains(row + column) && !z_diagonal.contains(row - column)) {
col.add(column);
f_diagonal.add(row + column);
z_diagonal.add(row - column);
char[] s = new char[NumOfQueen];
Arrays.fill(s, '.');
//这一行的j位置放皇后
s[column] = 'Q';
tmplist.add(new String(s));
//回溯算法
backtrack(row+1,NumOfQueen,res,tmplist,col,z_diagonal,f_diagonal);
tmplist.remove(tmplist.size() - 1);
col.remove(Integer.valueOf(column));
f_diagonal.remove(Integer.valueOf(row + column));
z_diagonal.remove(Integer.valueOf(row - column));
}
}
}

}

leetcode_【52】N皇后 II

https://leetcode-cn.com/problems/n-queens-ii/

  n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
  给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
  每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入:

  4
输出:
  2
解释:4皇后存在两种不同的解法
    [
    [“.Q..”, // 解法 1
    “…Q”,
    “Q…”,
    “..Q.” ],
    [ “..Q.”, // 解法 2
    “Q…”,
    “…Q”,
    “.Q..”]
    ]

如leetcode_【51】代码返回res.size();

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//执行用时
:29 ms, 在所有 Java 提交中击败了5.45%的用户
//内存消耗 :34.8 MB, 在所有 Java 提交中击败了41.25%的用户
public class Solution {
public static int totalNQueens(int n) {
//用于存储行
List<Integer> col = new ArrayList<>();
//用于存储正对角线
List<Integer> z_diagonal = new ArrayList<>();
//用于存储负对角线
List<Integer> f_diagonal = new ArrayList<>();
//存储结果
List<List<String>> res = new ArrayList<>();
//从第一个row = 0开始
backtrack(0, n, res, new ArrayList<String>(), col, z_diagonal, f_diagonal);
return res.size();
}
public static void backtrack(int row, int NumOfQueen, List<List<String>> res, ArrayList<String> tmplist, List<Integer> col, List<Integer> z_diagonal, List<Integer> f_diagonal) {
//到达了最后一行
if (row == NumOfQueen) {
res.add(new ArrayList<>(tmplist));
return;
}
//从第0列开始遍历
for (int column = 0; column < NumOfQueen; column++) {
//如果不在攻击范围内(不在同一行或者同一列 && 负对角线和相等 && 正对角线差相等)
if (!col.contains(column) && !f_diagonal.contains(row + column) && !z_diagonal.contains(row - column)) {
col.add(column);
f_diagonal.add(row + column);
z_diagonal.add(row - column);
char[] s = new char[NumOfQueen];
Arrays.fill(s, '.');
//这一行的j位置放皇后
s[column] = 'Q';
tmplist.add(new String(s));
//回溯算法
backtrack(row+1,NumOfQueen,res,tmplist,col,z_diagonal,f_diagonal);
tmplist.remove(tmplist.size() - 1);
col.remove(Integer.valueOf(column));
f_diagonal.remove(Integer.valueOf(row + column));
z_diagonal.remove(Integer.valueOf(row - column));
}
}
}
}

leetcode_【78】子集I

https://leetcode-cn.com/problems/subsets/

 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
 说明:解集不能包含重复的子集。
 示例:
输入:
  nums = [1,2,3]
输出:
   [
   [3],
   [1],
   [2],
   [1,2,3],
   [1,3],
   [2,3],
   [1,2],
   []
   ]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//执行用时 :2 ms, 在所有 Java 提交中击败了87.86%的用户
//内存消耗 :37 MB, 在所有 Java 提交中击败了44.12%的用户
class Solution {
public static List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
backTrace(nums,0,new ArrayList<>(),res);
return res;
}

private static void backTrace(int[] nums, int start, ArrayList<Integer> tmplist, List<List<Integer>> res) {
res.add(new ArrayList<>(tmplist));
for(int i = start;i<nums.length;i++){
tmplist.add(nums[i]);
backTrace(nums,i+1,tmplist,res);
tmplist.remove(tmplist.size()-1);
}
}
}

leetcode_【90】子集 II

https://leetcode-cn.com/problems/subsets-ii/

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入:
  [1,2,2]
输出:
    [
    [2],
    [1],
    [1,2,2],
    [2,2],
    [1,2],
    []
    ]

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//执行用时 :3 ms, 在所有 Java 提交中击败了87.52%的用户
//内存消耗 :38.5 MB, 在所有 Java 提交中击败了47.42%的用户
class Solution {
public static List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
backTrace(nums,0,new ArrayList<>(),res);
return res;
}
private static void backTrace(int[] nums, int start, ArrayList<Integer> tmplist, List<List<Integer>> res) {
res.add(new ArrayList<>(tmplist));
for(int i = start;i<nums.length;i++){
//和上一个数字相等则跳过
if(i > start && nums[i] == nums[i-1] ){
continue;
}else{
tmplist.add(nums[i]);
backTrace(nums,i+1,tmplist,res);
tmplist.remove(tmplist.size()-1);
}

}
}
}
文章目录
  1. 1. 回溯算法
  2. 2. 如何使用回溯算法
  3. 3. leetcode_【46】全排列 I
    1. 3.1. 解题思路:
    2. 3.2. 代码
  4. 4. leetcode_【47】全排列 II
    1. 4.1. 代码
  5. 5. leetcode_【77】组合
  6. 6. leetcode_【39】组合总和 I
    1. 6.1. 代码
  7. 7. leetcode_【40】组合总和 II
    1. 7.1. 代码
  8. 8. leetcode_【216】组合总和III
    1. 8.1. 代码
  9. 9. leetcode_【377】组合总和 IV
    1. 9.1. 代码
  10. 10. leetcode_【51】N皇后 I
    1. 10.1. 代码
  11. 11. leetcode_【52】N皇后 II
    1. 11.1. 代码
  12. 12. leetcode_【78】子集I
    1. 12.1. 代码
  13. 13. leetcode_【90】子集 II
    1. 13.1. 代码
| 139.6k