回溯法概念:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点
出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过
对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来
求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。
概念:http://www.cnblogs.com/jiangchen/p/5393849.html
1. Combination Sum
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
- All numbers (including target) will be positive integers.
- The solution set must not contain duplicate combinations.
For example, given candidate set [2, 3, 6, 7]
and target 7
,
A solution set is:
[
[7],
[2, 2, 3]
]
刚做的时候的思路:
用for循环+ 递归的方式求解
for 循环套在外层,表示遍历数组的第i个数字
内层递归表示结果list里的第i个数字(比如第一层递归是寻找list里的第一个数字)
归纳:其实这就是回溯法做的一个dfs,找到第一个节点,然后再找第二个节点,当固定第一个节点的所有解找到后,再换另外个点做为第一个节点。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 public List<List<Integer>> combinationSum(int[] candidates, int target) { 3 int size = candidates.length; 4 List<List<Integer>> res = new ArrayList<List<Integer>>(); 5 List<Integer> list = new ArrayList<Integer>(); 6 helper (res, list, candidates, target, 0); 7 return res; 8 } 9 private static void helper(List<List<Integer>> res,List<Integer> list, int[] candidates, int target, int pos) { 10 if (target == 0) { 11 res.add(new ArrayList<Integer>(list)); 12 return; 13 14 } 15 16 if (target < 0) { 17 return; 18 } 19 int prev = -1; 20 for (int i = pos; i < candidates.length; i++) { 21 if(prev == candidates[i]) { 22 continue; 23 } 24 list.add(candidates[i]); 25 helper(res, list, candidates, target - candidates[i], i); 26 prev = candidates[i]; 27 list.remove(list.size() - 1); 28 } 29 } 30 }
---------------我是分割线--------------------------------------
Combination Sum 2
Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
Note:
- All numbers (including target) will be positive integers.
- The solution set must not contain duplicate combinations.
For example, given candidate set [10, 1, 2, 7, 6, 1, 5]
and target 8
,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
这个题目要注意下面的错误情况
用递归寻找第一个元素时,如果之前的结果里已经把1当作第一个元素,然后pop出去了,那么下次的1再想做为第一个元素的话就直接continue,这样就避免了2个[1,2,5]的情况,也就是说,在for循环里面不允许有相同的元素!只有第一次出现才做为valid
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 public List<List<Integer>> combinationSum2(int[] candidates, int target) { 3 int size = candidates.length; 4 Arrays.sort(candidates); 5 List<List<Integer>> res = new ArrayList<List<Integer>>(); 6 List<Integer> list = new ArrayList<Integer>(); 7 helper (res, list, candidates, target, 0); 8 return res; 9 } 10 private static void helper(List<List<Integer>> res,List<Integer> list, int[] candidates, int target, int pos) { 11 if (target == 0) { 12 res.add(new ArrayList<Integer>(list)); 13 return; 14 15 } 16 if (target < 0) { 17 return; 18 } 19 int prev = -1; 20 for (int i = pos; i < candidates.length; i++) { 21 if (candidates[i] > target) { 22 break; 23 } 24 25 if (prev != -1 && prev == candidates[i]) { 26 continue; 27 } 28 29 if (candidates[i] != -1) { 30 list.add(candidates[i]); 31 helper(res, list, candidates, target - candidates[i], i + 1); 32 prev = candidates[i]; 33 list.remove(list.size() - 1); 34 } 35 } 36 } 37 }
---------------我是分割线--------------------------------------
Combination Sum 3
Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.
Example 1:
Input: k = 3, n = 7
Output:
[[1,2,4]]
Example 2:
Input: k = 3, n = 9
Output:
[[1,2,6], [1,3,5], [2,3,4]]
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 public List<List<Integer>> combinationSum3(int k, int n) { 3 List<List<Integer>> res = new ArrayList<List<Integer>>(); 4 List<Integer> list = new ArrayList<Integer>(); 5 helper (res, list, k, n, 1); 6 return res; 7 } 8 private static void helper(List<List<Integer>> res,List<Integer> list, int k, int n, int current) { 9 if (n == 0 && list.size() == k) { 10 res.add(new ArrayList<Integer>(list)); 11 return; 12 13 } 14 if (list.size() == k || n < 0) { 15 return; 16 } 17 18 19 for (int i = current; i < 10; i++) { 20 list.add(i); 21 helper(res, list, k, n - i, i + 1); 22 list.remove(list.size() - 1); 23 } 24 } 25 }
相当于[1,2,3,4,5,6,7,8,9] 的数组,然后增加了k的控制条件
---------------我是分割线--------------------------------------
Combination Sum IV
Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.
Example:
nums = [1, 2, 3]
target = 4
The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
Note that different sequences are counted as different combinations.
Therefore the output is 7.
Follow up:
What if negative numbers are allowed in the given array?
How does it change the problem?
What limitation we need to add to the question to allow negative numbers?
巨坑,只需要返回结果个数,那么其实是个动态规划的题目,类似于爬楼梯!!!
就他娘的这么简单 = =
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 public int combinationSum4(int[] nums, int target) { 3 int size = nums.length; 4 Arrays.sort(nums); 5 int[] dp = new int[target + 1]; 6 dp[0] = 1; 7 for (int i = 0; i <= target; i++) { 8 for (int temp : nums) { 9 if (i + temp > target) { 10 break; 11 } 12 dp[i + temp] += dp[i]; 13 } 14 } 15 return dp[target]; 16 17 } 18 }
---------------我是分割线--------------------------------------
来一枚简单的
Combinations
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
同样的套路
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 public List<List<Integer>> combine(int n, int k) { 3 List<List<Integer>> res = new ArrayList<List<Integer>>(); 4 List<Integer> path = new ArrayList<Integer>(); 5 if (n < k) { 6 return res; 7 } 8 helper(res, path, n, k, 1); 9 return res; 10 } 11 private void helper (List<List<Integer>> res, List<Integer> path, int n, int k, int current) { 12 if (path.size() == k) { 13 res.add(new ArrayList<Integer>(path)); 14 return; 15 } 16 for (int i = current; i <= n; i++) { 17 path.add(i); 18 helper(res, path, n, k, i + 1); 19 path.remove(path.size() - 1); 20 } 21 } 22 }
---------------我是分割线--------------------------------------
Generate Parentheses
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
[ "((()))", "(()())", "(())()", "()(())", "()()()" ]
可以用left,right 分别表示左括号和右括号的个数。 我的解法用了valid的int value, 当左括号时+1,右括号时-1. 确保valid大于0
这个for循环也可以不要,直接用做2次,一次left,一次right来代替
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 public List<String> generateParenthesis(int n) { 3 StringBuilder sb = new StringBuilder(); 4 List<String> res = new ArrayList<String>(); 5 int valid = 0; 6 char[] chars = {'(',')'}; 7 helper (sb, res, valid, chars, n); 8 return res; 9 } 10 private static void helper(StringBuilder sb, List<String> res, int valid, char[] chars, int n) { 11 if (valid < 0) { 12 return; 13 } 14 if (sb.length() == 2*n ) { 15 if(valid == 0) { 16 res.add(sb.toString()); 17 } 18 return; 19 } 20 for (char temp : chars) { 21 sb.append(temp); 22 int tempVal = temp == '(' ? 1 : - 1; 23 valid += tempVal; 24 helper (sb, res, valid, chars, n); 25 valid -= tempVal; 26 sb.deleteCharAt(sb.length() - 1); 27 } 28 } 29 }
附上九章的答案,就是这么简洁= =
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 public ArrayList<String> generateParenthesis(int n) { 3 ArrayList<String> result = new ArrayList<String>(); 4 if (n <= 0) { 5 return result; 6 } 7 helper(result, "", n, n); 8 return result; 9 } 10 11 public void helper(ArrayList<String> result, 12 String paren, // current paren 13 int left, // how many left paren we need to add 14 int right) { // how many right paren we need to add 15 if (left == 0 && right == 0) { 16 result.add(paren); 17 return; 18 } 19 20 if (left > 0) { 21 helper(result, paren + "(", left - 1, right); 22 } 23 24 if (right > 0 && left < right) { 25 helper(result, paren + ")", left, right - 1); 26 } 27 } 28 }
模仿九章的版本
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 /** 3 * @param n n pairs 4 * @return All combinations of well-formed parentheses 5 */ 6 public ArrayList<String> generateParenthesis(int n) { 7 // Write your code here 8 ArrayList<String> res = new ArrayList<String>(); 9 StringBuilder sb = new StringBuilder(); 10 int left = 0, right = 0; 11 helper (res, sb , n, left, right); 12 return res; 13 } 14 private static void helper(List<String> res, StringBuilder sb, int n, int left, int right) { 15 if (sb.length() == 2 * n) { 16 if (left == right) { 17 res.add(sb.toString()); 18 } 19 return; 20 } 21 if (left >= right) { 22 sb.append('('); 23 helper(res, sb, n, left + 1, right); 24 sb.deleteCharAt(sb.length() - 1); 25 } 26 if (left > right) { 27 sb.append(')'); 28 helper(res, sb, n, left, right + 1); 29 sb.deleteCharAt(sb.length() - 1); 30 31 } 32 } 33 }
---------------我是分割线--------------------------------------
Letter Combinations of a Phone Number
Given a digit string, return all possible letter combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below.
Input:Digit string "23" Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class Solution { 2 public List<String> letterCombinations(String digits) { 3 StringBuilder sb = new StringBuilder(); 4 List<String> res = new ArrayList<String>(); 5 if (digits == null || digits.length() == 0) { 6 return res; 7 } 8 HashMap<Character, String> map = new HashMap<Character, String>(); 9 map.put ('2', "abc"); 10 map.put ('3', "def"); 11 map.put ('4', "ghi"); 12 map.put ('5', "jkl"); 13 map.put ('6', "mno"); 14 map.put ('7', "pqrs"); 15 map.put ('8', "tuv"); 16 map.put ('9', "wxyz"); 17 helper (digits, map, sb, res, 0); 18 return res; 19 } 20 private static void helper (String digits, HashMap<Character, String> map, StringBuilder sb, List<String> res, int current) { 21 if (sb.length() == digits.length()) { 22 res.add(sb.toString()); 23 return; 24 } 25 String temp = map.get(digits.charAt(current)); 26 for (int i = 0; i < temp.length(); i++) { 27 sb.append(temp.charAt(i)); 28 helper (digits, map, sb, res, current + 1); 29 sb.deleteCharAt(sb.length() - 1); 30 } 31 } 32 }