算法训练day19 | php | 理论基础 ,77. 组合

一、理论基础

        回溯法也叫回溯搜索法。回溯法的本质是穷举,穷举所有可能,从中找到想要的答案。回溯法效率其实并不高,但可以通过剪枝操作适当地提高一些效率。

        因为部分问题即使暴力搜索也很难搜索出来,所以需要用到回溯法。例如今天的组合问题,N个数里面按一定规则找出k个数的集合,如果直接用for循环,就要用到N重for循环,直接写肯定是不行的,就需要用到递归回溯。

        回溯是递归的副产品,只要有递归就会有回溯。

二、力扣题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]]

提示:

  • 1 <= n <= 20
  • 1 <= k <= n

        一般解决此类问题,都会先把问题抽象为树形结构(即n叉树),用这种结构来理解回溯的过程会简单很多。

        因为时间比较紧,不想自己作图,引用代码随想录卡哥的树形图:

        第一层按顺序取 [ 1, 4 ]中的数延伸一条枝干,再在第二层依次各取剩下的数的其中一个数延伸一条枝干,依照组合的特性,每次取数时只能取比当前取得的数更大的数。

        此时需要添加两个变量。一个是数组 path:用于记录根节点到叶子节点取得的值。另一个是变量 cur:用于记录当前按顺序遍历到哪个数。

        而回溯的作用正是用于使数组 path 记录根节点到叶子节点的每一条路径。

        例如:path数组在第一层取 1 放入数组后,进入下一层从 2,3,4 中取得 2 放入数组,此时已到达叶子节点,把 path 数组存入结果集。然后回退到第二层,此时再把 2 从 path数组中取出来,重新从 3,4 中取得 3 放入数组。

        即每次回退到上一层时,都要把 path 数组的最后一个元素取出丢弃,然后重新选取新的元素放入path 数组,这样才能保证回溯效果。

        代码如下,在回溯函数中需要添加 是否是叶子节点的判断,是的话把 path 存入全局变量数组result中 。

        还添加了剪枝操作,即如果当前已选的值和接下来可选的值的个数加起来 < k,则这条路径绝对不可能得到符合条件的数组,可以直接中断。例如上面的 n = 4, k = 2,当第一次取 4 时,接下来已经没有可选的值了,所以肯定达不到数组个数为 k 的要求,就可以直接中断。

class Solution {
    private $result = [];
    /**
     * @param Integer $n
     * @param Integer $k
     * @return Integer[][]
     */
    function combine($n, $k) {
        $path = [];
        $this->backtracking($n, $k, $path, 1);
        return $this->result;
    }

    //$path 记录遍历的组合,$cur 记录当前遍历到的值
    function backtracking($n, $k, $path, $cur) {
        if(count($path) == $k) {        //判断叶子节点
            $this->result[] = $path;
            return;
        }
        if($n - $cur + 1 + count($path) < $k) {        //剪枝操作
            return;
        }

        for($i = $cur; $i <= $n; $i++) {
            $path[] = $i;
            $this->backtracking($n, $k, $path, $i + 1);
            array_pop($path);        //回溯操作,将path的最后一个元素删除
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值