【前端er每日算法】回溯法第一题--77.组合

71 阅读2分钟

题目

77. 组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

  示例 1:

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

输入:n = 1, k = 1
输出:[[1]]

思路

求k个数的组合,每次选一个数字,然后在剩余数组中,再选一个,然后直到选到了k个数字,就本次收集完毕,然后开始回退,接着遍历下一个数字,直到遍历完。

回溯三步曲:

  • 递归函数返回值和参数:无返回值,但需要全局变量存放结果,以及当前遍历路径
  • 递归终止条件:当path的长度等于k的时候,说明找到了一条路径,将当前path放到result结果里
  • 单层递归逻辑:遍历数组,每次push当前元素到path中,然后递归从后面数组中选一个元素,需要几个元素,会深度递归几层,是用递归来模拟了暴力解法的k个循环。
var combine = function(n, k) {
    let path = [];
    let result = [];
    const backTracking = function(n, k, startIndex) {
        if (path.length === k) {
            // 这里注意要拷贝,否则最后都指向了path一个引用,会出现不对的情况
            result.push(path.slice(0)); 
            return;
        }
        for (let i = startIndex; i < n; i++) {
            path.push(i+1)
            backTracking(n, k, i + 1);
            path.pop();
        }
    }
    backTracking(n, k, 0);
    return result;
};

然后,剪枝,这里面当遍历数组时,剩下的元素不足k-path.length时,无需再继续遍历,所以可以把循环条件优化一下,如下:

var combine = function(n, k) {
    let path = [];
    let result = [];
    const backTracking = function(n, k, startIndex) {
        if (path.length === k) {
            result.push(path.slice(0));
            return;
        }
        // 这里的终止条件缩小了,省去了一些操作
        for (let i = startIndex; i < n - (k - path.length) + 1; i++) {
            path.push(i+1)
            backTracking(n, k, i + 1);
            path.pop();
        }
    }
    backTracking(n, k, 0);
    return result;
};

总结

写的时候发现,当你看了视频后,你觉得你懂了,但是当你写的时候,好像不对,没写出来,然后写出来后,如果你要写一个文章总结下,发现,咦,好像还是有地方理解错了,学习就是不断的扣细节,不断加深,不断熟练的过程呀。

ps:今天终于写了一道题了,可以打卡了==,哭死,上周忙的无精打采,在gq写代码就是谁都觉得不重要,而且就不需要多久就可以开发完,真是奇怪了,在互联网公司代码那么重要的资产,程序员就是核心,可是在gq,完全就不一样了,我想了想,还是跟企业性质不同,差别太大导致,所以互联网run gq,除非不想搞技术了,否则就别来,你会难受每一天。