java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 |
---|
- 这种题只能暴力求解,枚举所有可能得组合
- 例如要找2个数的组合,那么就两层for循环,3个数的就3层for循环,k层就k个for循环
- 但是这样显然不现实,所以我们可以使用回溯的思路,利用递归,只写一层for循环,就可以实现普通方法下,k层for循环的效果。
- 当然,效率是一样的。但是代码实现起来,嵌套for循环可没法实现,试想k = 50,就得写50层for循环。
- 我们要进行k个数的组合,这些数字只能从1-n中去选。也就是从1-n中调出k个不同的数,组合起来。
- 第一个位置,可以选择1-n,从1开始,每个数都需要作为第1个位置的数,进行枚举
- 既然是穷举,如果第一个位置当前从3开始,那么第二个位置就从4开始枚举,因为前面的几个数组,一定已经在前几次第一个位置为1,2的时候就组合过了
- 所以,每一个位置,都要尝试可选的每一种可能。
- 如果当前已经组合的数,和剩下可以参与组合的数,不够组合成一个长度为k的组合
- 例如我们k = 4,目前组合出来的是[4,8],剩下可以组合就剩下一个9了,最多组合成一个[4,8,9],根本不够4个数
- 遇到这种情况,就不需要继续枚举当前情况了。俗称枝剪操作。
1. 递归实现
代码:基本上是官方增加大量测试用例后,目前优化到的比较快的了。最前面4ms到9ms的,是远古时期提交的人,它们的代码,放到现在,只能跑到35ms,超越25%的人 |
---|
- 这个是比较难理解的版本,没有使用经典的回溯模版(递归套for,for套递归),但是这个做法的速度更快
class Solution {List<List<Integer>> result;int k;int n;public List<List<Integer>> combine(int n, int k) {this.n=n;this.k=k;result=new ArrayList<List<Integer>>();Integer[] records = new Integer[k];traversal(0,1,records);return result;}public void traversal(int row,int column,Integer[] records){if (column>n){return;}else if(records.length + (n-column+1)<k) {return;}else {records[row]=column;if (row==k-1){result.add(List.of(records));}else {traversal(row+1,column+1, records);}traversal(row, ++column, records);}}
}
- 经典回溯模版
class Solution {public List<List<Integer>> combine(int n, int k) {List<List<Integer>> res = new ArrayList<>();if (k <= 0 || n < k) return res;ArrayList<Integer> path = new ArrayList<>();dfs(n, k, 1, path, res);return res;}void dfs(int n, int k, int begin, ArrayList<Integer> path, List<List<Integer>> res) {int size = path.size();if (size == k) {res.add(new ArrayList<>(path));return;}for (int i = begin; i <= n - (k - size) + 1; i++) {path.add(i);dfs(n, k, i + 1, path, res);path.removeLast();if(path.size()+(n-begin) < k) return;}}
}