LeetCode 77 _ Combinations 组合
Description:
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
Example:
Input: n = 4, k = 2 Output: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
Solution:
这道题让我们输出从1~n中抽出k个数的所有可能性
这道题看着很熟悉,一看又是老套路,一层一层解题。
举个栗子,例如n=4,k=2时,
我们首先寻找1开头:将1放入一个临时数组中,然后在剩下的数中寻找满足条件的数,如2,再加入到临时数组中;
这时我们已满足了题目中“抽出k(2)个数”的要求,那么可以将临时数组[1,2]加入到结果中了。
再进行下一个满足条件的数组的获取。
将临时数组中的最后一位删除,在首次搜寻后的剩下的数组中继续按上述方法寻找,得到“1,3;1,4”。按此方法搜寻,直到将所有可能情况遍历到。
我们首先确定第一个数,接着从剩下的数中找出第二个数,若还需要第三位,则再从剩下的数中找出第三位数……
当填满数字以后(即填入了的数字个数为k),将该数组加入到结果List中,然后删掉最后一位数,再尝试其他可能。
这样的方法叫做 backtracking ,中文为回溯,在网上找了一下相关定义,贴在下面:
回溯算法的定义:回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。适用于求解组合数较大的问题。
对于回溯问题,总结出一个递归函数模板,包括以下三点
· 递归函数的开头写好跳出条件,满足条件才将当前结果加入总结果中
· 已经拿过的数不再拿 if(s.contains(num)){continue;}
· 遍历过当前节点后,为了回溯到上一步,要去掉已经加入到结果list中的当前节点。
针对不同的题目采用不同的跳出条件或判断条件。
在LeetCode中回溯算法使用到的题目有很多,就目前我做的几十题来说已经遇到了好多次,是一个需要重点掌握的内容喔~
Code:
public List<List<Integer>> combine(int n, int k) { List<List<Integer>> res = new ArrayList<>(); combineSub(res, new ArrayList<>(), 1, n, k); return res; } private void combineSub(List<List<Integer>> res, List<Integer> temp, int start, int n, int k){ if (k == 0){ res.add(new ArrayList<>(temp)); return; } for (int i = start; i <= n; i++){ temp.add(i); combineSub(res, temp, i+1, n, k-1); temp.remove(temp.size()-1); } }
提交情况:
Runtime: 24 ms, faster than 65.89% of Java online submissions for Combinations.
Memory Usage: 39.9 MB, less than 99.75% of Java online submissions for Combinations.
参考资料:
https://leetcode.com/problems/combination-sum/discuss/16502/A-general-approach-to-backtracking-questions-in-Java-(Subsets-Permutations-Combination-Sum-Palindrome-Partitioning)