组合 77. 组合 https://leetcode.cn/problems/combinations/
for循环 i确定开头(第一层)的数字 循环里面进行递归 确定第二层的数字 当遍历到第k层 就结束 使用tag标记下一层的开头数字
使用公式 n-i+1 + list.size() >= k 进行减枝 n-i+1 表示剩下可选的数字 ,list.size() 表示当前已选择的数,加起来不能小于 k
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { List<Integer> list = new ArrayList<>(); List<List<Integer>> res = new ArrayList<>(); public List<List<Integer>> combine(int n, int k) { dfs(n, k, 1 ); return res; } public void dfs (int n, int k, int tag) { if (list.size() == k) { res.add(new ArrayList<>(list)); return ; } for (int i = tag; n-i+1 + list.size() >= k; i++) { list.add(i); dfs(n, k, i+1 ); list.remove(list.size()-1 ); } } }
216. 组合总和 III https://leetcode.cn/problems/combination-sum-iii/ 和上一题一样 for循环确定第一层的数字 里面递归确定下一层的数字 也可以跳过该层(也就是回溯list.remove(list.size()-1);
) 只是加了一个 和要为n 的条件 剪枝 i <= n && i <= 9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> combinationSum3(int k, int n) { dfs(k, 1 , n); return res; } public void dfs (int k, int begin, int n) { if (list.size() == k && n == 0 ) { res.add(new ArrayList<Integer>(list)); return ; } for (int i = begin; i <= n && i <= 9 ; i++) { list.add(i); dfs(k, i+1 , n-i); list.remove(list.size()-1 ); } } }
17. 电话号码的字母组合 https://leetcode.cn/problems/letter-combinations-of-a-phone-number/description/ for循环决定 第一层数组的元素选择 (例如{a, b, c}中选哪一个) 里面的递归 表示进入下层数组 ({d,e, f})
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 class Solution { String[][] arr = new String[][]{ {"a" ,"b" ,"c" },{"d" ,"e" ,"f" }, {"g" ,"h" ,"i" },{"j" ,"k" ,"l" },{"m" ,"n" ,"o" }, {"p" ,"q" ,"r" ,"s" },{"t" ,"u" ,"v" },{"w" ,"x" ,"y" ,"z" } }; List<String> res = new ArrayList<>(); List<String> list = new ArrayList<>(); public List<String> letterCombinations (String digits) { int k = digits.length(); if (k != 0 ) dfs(k, 0 , digits); return res; } public void dfs (int k, int begin, String digits) { if (list.size() == k) { StringBuffer str = new StringBuffer(); int size = list.size(); for (int i = 0 ; i <= size - 1 ; i++) { str.append(list.get(i)); } res.add(new String(str)); return ; } for (int i = 0 ; i <= arr[digits.charAt(begin)-'2' ].length-1 ; i++) { list.add(arr[digits.charAt(begin)-'2' ][i]); dfs(k, begin+1 , digits); list.remove(list.size()-1 ); } } }
39. 组合总和 (可重复选取) https://leetcode.cn/problems/combination-sum/description/ for循环确定第一层的数字 里面递归确定下一层的数字确定下一层的 begin不需要+1,因为可以重复选取
剪枝: target >= candidates[i]
先将数组排序 之后 只要当前的要选取的数大于还需要的总和 target 就结束 因为已经从小到大排序了 当前的数 大于target 那么之后数组中的数 也一定也大于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> combinationSum(int [] candidates, int target) { Arrays.sort(candidates); dfs(candidates, target, 0 ); return res; } public void dfs (int [] candidates, int target, int begin) { if (target == 0 ) res.add(new ArrayList<>(list)); if (target <= 0 ) return ; for (int i = begin; i <= candidates.length-1 && target >= candidates[i]; i++) { list.add(candidates[i]); dfs(candidates, target-candidates[i], i); list.remove(list.size()-1 ); } } }
40. 组合总和 II (有重复元素) https://leetcode.cn/problems/combination-sum-ii/description/
和 组合总和 思路一致 重点就是 同层的元素选取不能重复选取 所以加入一个条件 i > begin && candidates[i] == candidates[i-1]
同层遇到相同的元素 就跳过(前提 要相见数组排序)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> combinationSum2(int [] candidates, int target) { Arrays.sort(candidates); dfs(candidates, target, 0 ); return res; } public void dfs (int [] candidates, int target, int begin) { if (target == 0 ) res.add(new ArrayList<>(list)); if (target <= 0 ) return ; for (int i = begin; i <= candidates.length-1 && target >= candidates[i]; i++) { if (i > begin && candidates[i] == candidates[i-1 ]) continue ; list.add(candidates[i]); dfs(candidates, target - candidates[i], i+1 ); list.remove(list.size()-1 ); } } }
分割 切割问题类似组合问题
131. 分割回文串 https://leetcode.cn/problems/palindrome-partitioning/description/
for循环选择如何分割 如:a,ad 还是 aa,b 还是 aab, 递归进入下一层选择 上一层分割后右边的部分如何分割 如:a,a,d 还是 a,ad, 逗号,
代表分割 代码中begin表示 当begin到达字符串最后面代表结束
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 class Solution { List<List<String>> res = new ArrayList<>(); List<String> list = new ArrayList<>(); public List<List<String>> partition(String s) { dfs(s, 0 ); return res; } public void dfs (String s, int begin) { if (begin == s.length()) { res.add(new ArrayList<>(list)); return ; } for (int i = begin+1 ; i <= s.length(); i++) { if (!isHui(s, begin, i-1 )) continue ; list.add(s.substring(begin, i)); dfs(s, i); list.remove(list.size()-1 ); } } public boolean isHui (String str, int l, int r) { while (l < r) { if (str.charAt(l) != str.charAt(r)) return false ; l++; r--; } return true ; } }
93. 复原 IP 地址 https://leetcode.cn/problems/restore-ip-addresses/description/
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 class Solution { List<String> res = new ArrayList<>(); StringBuffer strBuff = new StringBuffer(); public List<String> restoreIpAddresses (String s) { dfs(s, 0 , 0 ); return res; } public void dfs (String s, int begin, int num) { if (num == 4 ) { if (begin == s.length()) res.add(strBuff.deleteCharAt(begin+3 ).toString()); return ; } for (int i = begin+1 ; i <= s.length() && i <= begin+3 ; i++) { String str = s.substring(begin, i); if (!isTrue(str)) continue ; strBuff.append(str+"." ); dfs(s, i, num+1 ); strBuff.delete(begin+num, i+num+1 ); } } public boolean isTrue (String str) { if (Integer.parseInt(str) > 255 || (str.length() > 1 && str.charAt(0 ) == '0' )) return false ; return true ; } }
子集 和组合问题没有什么区别, 唯一不同就是加入res时候 是每个节点都加入res中,而组合问题是将叶子节点才加入res,需要一个判断if
78. 子集 https://leetcode.cn/problems/subsets/description/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> subsets(int [] nums) { dfs(nums, 0 ); return res; } public void dfs (int [] nums, int begin) { res.add(new ArrayList<>(list)); for (int i = begin; i <= nums.length-1 ; i++) { list.add(nums[i]); dfs(nums, i+1 ); list.remove(list.size()-1 ); } } }
90. 子集 II (有重复元素) https://leetcode.cn/problems/subsets-ii/description/ 同层的元素选取不能重复选取 ,加一个条件 if(i > begin && nums[i] == nums[i-1])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> subsetsWithDup(int [] nums) { Arrays.sort(nums); dfs(nums, 0 ); return res; } public void dfs (int [] nums, int begin) { res.add(new ArrayList<>(list)); for (int i = begin; i <= nums.length-1 ; i++) { if (i > begin && nums[i] == nums[i-1 ]) continue ; list.add(nums[i]); dfs(nums, i+1 ); list.remove(list.size()-1 ); } } }
491. 递增子序列(排除同层重复元素 方法2) https://leetcode.cn/problems/non-decreasing-subsequences/ 因为要求子序列,所以 不能再通过将数组进行排序,然后i > begin && nums[i] == nums[i-1]
来排除同层重复元素 这道题如果通过使用res.contians()
来排除重复list会超时 所以 只能使用一个集合或者数组来记录同层的元素是否重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> findSubsequences(int [] nums) { dfs(nums, 0 ); return res; } public void dfs (int [] nums, int begin) { if (list.size() >= 2 ) res.add(new ArrayList<>(list)); List<Integer> temp = new ArrayList<>(); for (int i = begin; i <= nums.length-1 ; i++) { if (temp.contains(nums[i]) || list.size() > 0 && nums[i] < list.get(list.size()-1 )) continue ; temp.add(nums[i]); list.add(nums[i]); dfs(nums, i+1 ); list.remove(list.size()-1 ); } } }
排列 46. 全排列 https://leetcode.cn/problems/permutations/description/
和组合的区别在于 结果的每个list里面的元素不是按照nums数组元素的顺序 可以是无序的, 所以,下一层的元素选择 只需要判断是否已经选取过, 可以使用数组判断 也可以使用list里面的方法直接进行判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> permute(int [] nums) { dfs(nums); return res; } public void dfs (int [] nums) { if (list.size() == nums.length) { res.add(new ArrayList<>(list)); return ; } for (int i = 0 ; i <= nums.length-1 ; i++) { if (list.contains(nums[i])) continue ; list.add(nums[i]); dfs(nums); list.remove(list.size()-1 ); } } }
47. 全排列 II (排除同层重复元素) https://leetcode.cn/problems/permutations-ii/description/
首先 要排除同层重复元素, 其次 下一层要排除已经选过的元素 使用一个数组arr 如果该元素被选 就将该位置标记为true, 回溯时 标记回false 进入下一层后 先判断同层元素是否重复 使用 i > 0 && nums[i] == nums[i-1]
(要先将数组排序), 并且 arr[i-1] == false
证明上一个相同元素和自己是同一层 ,如果以上条件都满足,就continue; 如何判断 当前元素上几层是否选过 arr[i] == false
如果选过 就跳过。
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 class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> list = new ArrayList<>(); boolean [] arr; public List<List<Integer>> permuteUnique(int [] nums) { arr = new boolean [nums.length]; Arrays.sort(nums); dfs(nums); return res; } public void dfs (int [] nums) { if (list.size() == nums.length) { res.add(new ArrayList<>(list)); return ; } for (int i = 0 ; i <= nums.length-1 ; i++) { if (i > 0 && nums[i] == nums[i-1 ] && arr[i-1 ] == false ) continue ; if (arr[i] == false ) { arr[i] = true ; list.add(nums[i]); dfs(nums); list.remove(list.size()-1 ); arr[i] = false ; } } } }