代码随想录【Day24】| 开始回溯!77. 组合

回溯法是一种通过穷举所有可能来解决问题的方法,其本质是递归地尝试所有路径并进行剪枝以提高效率。文章以77号组合问题为例,解释了如何使用回溯法找到所有可能的组合,强调了在添加元素到结果列表时要避免对原始对象的修改,以防止结果污染。通过剪枝操作,可以在一定程度上优化回溯法的性能。
摘要由CSDN通过智能技术生成

在这里插入图片描述
回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。

那么既然回溯法并不高效为什么还要用它呢?

因为没得选,一些问题能暴力搜出来就不错了,撑死了再剪枝一下,还没有更高效的解法。

回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!

因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度递归的深度构成的树的深度

递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

回溯模板:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

77. 组合

题目链接

题目描述:
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

难点:
剪枝
在这里插入图片描述

思路:

时间复杂度:O()
空间复杂度:O()

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
    public void backtracking(int n, int k, int startIdx) {
        if (n < 1 || n < k) return;
        if (path.size() == k) {
            result.add(new ArrayList<>(path));
            // result.add(path); 这是错误的!
            return;
        }
        for (int i = startIdx; i <= n; i++) {
            path.add(i);
            backtracking(n, k, i+1);
            path.remove(path.size()-1);
        }
    }
}

剪枝:

for (int i = startIdx; i <= n-(k-path.size())+1; i++) {
    path.add(i);
    backtracking(n, k, i+1);
    path.remove(path.size()-1);
}

时长:
15min

收获:
注意了!向列表中添加对象元素时,要考虑这个对象被添加后是否不能改变了。如果不能改变,必须要重新new一个对象,而不是指向原来的对象,否则结果集中的结果会产生改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值