一道工作中遇到的回溯算法题

问题描述

2020年有12个月,我们想得到如下格式的所有数据组合集合的结果:

[['2020-01'], ..., ['2020-12'],
['2020-01','2020-02'],...,['2020-11','2020-12'],
...
['2020-01',...,'2020-12']]

仔细观察上述目标结果,其实最终结果是一个二维数组,外层数组的元素正好是组合和 C 12 1 C_{12}^1 C121+…+ C 12 12 C_{12}^{12} C1212的所有可能结果。

解决思路

既然是得到所有可能的组合总和,那么一个很直接的思路是使用回溯算法。
知道思路后,我们进一步将问题进行分解,每一个 C 12 n C_{12}^n C12n都是一种回溯,而最外层的求和只是一个for循环而已,所以最终的问题在于如何得到每一个 C 12 n C_{12}^n C12n

求解组合

刷过力扣的朋友可能会遇到这种题,因为力扣上的第77题与此问题完全一致。有兴趣的朋友可以去力扣上查阅更多的解题方案:力扣原题。下面我以 C 12 2 C_{12}^2 C122为例子讲解我自己的解题思路。
首先,需要一个全局的数组来存储所有的满足条件组合情形,然后需要一个递归函数进行回溯。此递归函数的参数有组合下标(12),组合上标(2),动态数组以及遍历的起始坐标。此递归函数的终止条件为动态数组的长度等于组合上标。此递归函数的核心为,从起始坐标开始遍历,每次遍历都把新的元素添入动态数组中,然后进一步进行递归,当进一步递归结束时,删除掉动态数组中新增的元素,而这个过程就构成了回溯。最终当所有的回溯完成时,全局数组中就记录下的所有满足条件的组合情形。
Talk is cheap, show you my code:

const lists = [];
const year = '2020';
function main(n) {
    backtrack(n, 2, [], 1);
    console.log(lists);
    console.log(lists.length);
}
function backtrack(n, k, list, start) {
    if (list.length === k) {
        lists.push(formatData(list));
        return;
    }
    for (let i = start; i <= n; i++) {
        list.push(i);
        backtrack(n, k, list, i + 1);
        list.pop();
    }
}
function formatData(list) {
    const res = [];
    list.forEach(num => num < 10 ? res.push(`${year}-0${num}`) : res.push(`${year}-${num}`));
    return res;
}
main(12)

这里注意一个细节:正是由于所有的递归都是使用同一个动态数组,所以全局数组里的每次添入元素都需要进行深拷贝!

原题解答

把最大的问题解决后,那么原问题也便迎刃而解。
直接上代码:

const lists = [];
const year = '2020';
function main(n) {
    for (let i = 1; i <= n; i++) {
        backtrack(n, i, [], 1);
    }
    console.log(lists);
    console.log(lists.length);
}
function backtrack(n, k, list, start) {
    if (list.length === k) {
        lists.push(formatData(list));
        return;
    }
    for (let i = start; i <= n; i++) {
        list.push(i);
        backtrack(n, k, list, i + 1);
        list.pop();
    }
}
function formatData(list) {
    const res = [];
    list.forEach(num => num < 10 ? res.push(`${year}-0${num}`) : res.push(`${year}-${num}`));
    return res;
}
main(12)

结语

到此,又一道工作中的算法问题已被解决,虽然工作中很少会用到算法,但是保持持续熟练的算法功底依旧是很有必要,毕竟谁都会害怕书到用时方恨少的醒悟。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值