当前位置:网站首页>回溯——77. 组合
回溯——77. 组合
2022-07-25 23:55:00 【向着百万年薪努力的小赵】
1 题目描述
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/combinations
2 题目示例
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:
输入:n = 1, k = 1
输出:[[1]]
3 题目提示
1 <= n <= 20
1 <= k <= n
4 思路
重点概括:
- 如果解决—个问题有多个步骤,每一个步骤有多种方法,题目又要我们找出所有的方法,可以使用回溯算法;
- 回溯算法是在—棵树上的深度优先遍历(因为要找所有的解,所以需要遍历);
- 组合问题,相对于排列问题而言,不计较一个组合内元素的顺序性(即[1,2,3]与[1,3,2]认为是同一个组合),因此很多时候需要按某种顺序展开搜索,这样才能做到不重不漏。
既然是树形问题上的深度优先遍历,因此首先画出树形结构。例如输入:n = 4,k = 2,我们可以发现如下递归结构:
- 如果组合里有1,那么需要在[2,3,4]里再找1个数;
- 如果组合里有2,那么需要在[3,4]里再找1数。注意:这里不能再考虑1,因为包含1的组合,在第1种情况中已经包含。
依次类推(后面部分省略),以上描述体现的递归结构是:在以n结尾的候选数组里,选出若干个元素。
说明:
- 叶子结点的信息体现在从根结点到叶子结点的路径上,因此需要一个表示路径的变量path,它是一个列表,特别地,path是一个栈;
- 每一个结点递归地在做同样的事情,区别在于搜索起点,因此需要一个变量start ,表示在区间Lbegin, n]里选出若干个数的组合;
- 可能有一些分支没有必要执行,我们放在优化中介绍。
5 我的答案
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
public class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (k <= 0 || n < k) {
return res;
}
// 从 1 开始是题目的设定
Deque<Integer> path = new ArrayDeque<>();
dfs(n, k, 1, path, res);
return res;
}
private void dfs(int n, int k, int begin, Deque<Integer> path, List<List<Integer>> res) {
// 递归终止条件是:path 的长度等于 k
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
// 遍历可能的搜索起点
for (int i = begin; i <= n; i++) {
// 向路径变量里添加一个数
path.addLast(i);
// 下一轮搜索,设置的搜索起点要加 1,因为组合数理不允许出现重复的元素
dfs(n, k, i + 1, path, res);
// 重点理解这里:深度优先遍历有回头的过程,因此递归之前做了什么,递归之后需要做相同操作的逆向操作
path.removeLast();
}
}
}
优化
我们上面的代码,搜索起点遍历到 n,即:递归函数中有下面的代码片段:
// 从当前搜索起点 begin 遍历到 n
for (int i = begin; i <= n; i++) {
path.addLast(i);
dfs(n, k, i + 1, path, res);
path.removeLast();
}
事实上,如果n = 7,k = 4,从5开始搜索就已经没有意义了,这是因为:即使把5选上,后面的数只有6和7,一共就3个候选数,凑不出4个数的组合。因此,搜索起点有上界,这个上界是多少,可以举几个例子分析。
分析搜索起点的上界,其实是在深度优先遍历的过程中剪枝,剪枝可以避免不必要的遍历,剪枝剪得好,可以大幅度节约算法的执行时间。
容易知道:搜索起点和当前还需要选几个数有关,而当前还需要选几个数与已经选了几个数有关,即与path的长度相关。我们举几个例子分析:
例如: n = 6 , k = 4。
path.size() == 1的时候,接下来要选择3个数,搜索起点最大是4,最后一个被选的组合是[4,5,6];
path.size() == 2的时候,接下来要选择2个数,搜索起点最大是5,最后一个被选的组合是[5, 6];
path.size() == 3的时候,接下来要选择1个数,搜索起点最大是6,最后一个被选的组合是[6] ;
再如:n = 15 , k = 4。
path.size() == 1的时候,接下来要选择3个数,搜索起点最大是13,最后一个被选的是[13,14,15] ;
path.size() == 2的时候,接下来要选择2个数,搜索起点最大是14,最后一个被选的是[14, 15];
path.size() == 3的时候,接下来要选择1个数,搜索起点最大是15,最后一个被选的是[15] ;
可以归纳出:
搜索起点的上界+接下来要选择的元素个数一1 = n
其中,接下来要选择的元素个数= k - path.size(),整理得到:
搜索起点的上界 = n - (k - path.size()) + 1
所以,我们的剪枝过程就是:把i <= n改成i <= n - (k -path. size() +1 :
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
public class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (k <= 0 || n < k) {
return res;
}
Deque<Integer> path = new ArrayDeque<>();
dfs(n, k, 1, path, res);
return res;
}
private void dfs(int n, int k, int index, Deque<Integer> path, List<List<Integer>> res) {
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
// 只有这里 i <= n - (k - path.size()) + 1 与参考代码 1 不同
for (int i = index; i <= n - (k - path.size()) + 1; i++) {
path.addLast(i);
dfs(n, k, i + 1, path, res);
path.removeLast();
}
}
}
边栏推荐
- Unexpected dubug tricks
- R language installation tutorial | graphic introduction is super detailed
- Swap, move, forward, exchange of utility component learning
- Imitating the magnifying glass effect of JD products -- JS Foundation
- [testing technology automated testing pytest] basic summary of pytest
- Read the field status of account in ABAP code (hidden, optional, required)
- S4/HANA MM & SD EDI基于NAST的集成配置(ORDERS, ORDRSP, DESADV, INVOIC)
- Graph traversal DFS, BFS (code explanation)
- Quick sorting of top ten sorting
- What is the difference between'1 and'b1 when assigning values
猜你喜欢

Matchmaker's words

Key features and application trends of next generation terminal security management

抽丝剥茧C语言(高阶)程序环境和预处理

行为型模式之观察者模式

ArcGIS cuts TIF images (grid data) according to the vector range, merges shp files in batches, cuts vectors in the region according to the vector range, outputs the geographic coordinate system, conve

Numerical learning iota, accumulate
![[day.2] Joseph Ring problem, how to use arrays to replace circular linked lists (detailed explanation)](/img/2b/b354e52a9eb1d53475fa8d0339d33b.jpg)
[day.2] Joseph Ring problem, how to use arrays to replace circular linked lists (detailed explanation)

The process of finding free screen recording software - I didn't expect win10 to come with this function
![[nodejs] nodejs create a simple server](/img/00/7ab5629bf67777da21b41f44665880.png)
[nodejs] nodejs create a simple server

Bubble sort idea and Implementation
随机推荐
Key features and application trends of next generation terminal security management
[ManageEngine] servicedesk plus won the 2022 safety model engineering data safety award
【MUDUO】EventLoopThreadPool
[Muduo] thread package
@Autowired注解的底层原理
Nacos 下线服务时报错 errCode: 500
Topsis与熵权法
[testing technology automated testing pytest] basic summary of pytest
[Muduo] package EventLoop and thread
抽丝剥茧C语言(高阶)程序环境和预处理
《数据密集型应用系统设计》 - 应用系统概览
Shardingsphere data slicing
Vscode format JSON file
结对编程实践心得
利用用户脚本优化 Yandere/Konachan 站点浏览体验
如何用yolov5 做个闯红灯监控的智能交通系统(1)
Write a select drop-down list
Loading process such as reflection
Exercise (3) create a list set (both ArrayList and LinkedList)
The process of finding free screen recording software - I didn't expect win10 to come with this function