面试必看算法题 | 回溯算法解题框架

目录


1. 概述

1.1 回溯思想

回溯算法(Backtrack)是一种试错思想,本质上是深度优先搜索。即:从问题的某一种状态出发,依次尝试现有状态可以做出的选择从而进入下一个状态。递归这个过程,如果在某个状态无法做更多选择,或者已经找到目标答案时,则回退一步甚至多步重新尝试,直到最终所有选择都尝试过。

整个过程就像走迷宫一样,当我们遇到一个分叉口时,可以选择从一个方向进去尝试。如果走到死胡同则返回上一个分叉口,选择另外一条方向继续尝试,直到发现没有出口,或者找到出口。

1.2 回溯的三要素

理解了回溯算法的思想,下面我们来分析回溯的关键点。在回溯算法中,需要考虑三个要素:路径、选择列表、结束条件,以走迷宫为例:

  • 1. 路径:已经做出的选择
  • 2. 选择列表:当前状态可以做出的选择
  • 3. 结束条件:选择列表为空,或者找到目标

要走出这个迷宫,我们需要重复做一件事:选择从一个方向进去尝试。如果走到死胡同则返回上一个分叉口,选择另外一条方向继续尝试。用程序实现出来,这个重复做的事就是递归函数,实际中我们可以遵循一个解题模板 & 思路:

fun backtrack(){
    1\. 判断当前状态是否满足终止条件
    if(终止条件){
        return solution
    }
    2\. 否则遍历选择列表
    for(选择 in 选择列表){
        3\. 做出选择
        solution.push(选择)
        4\. 递归
        backtrack()
        5\. 回溯(撤销选择)
        stack.pop(选择)
    }
}

需要注意的是,解题框架 & 思路不是死板的,应根据具体问题具体分析。例如:回溯(撤销选择)并不是必须的,第 3.2 节第 k 个排序第 5 节岛屿数量问题中,它们在深层函数返回后没有必要回溯。

1.3 回溯剪枝

由于回溯算法的时间复杂度非常高,当我们遇到一个分支时,如果“有先见之明”,能够知道某个选择最终一定无法找到答案,那么就不应该去尝试走这条路径,这个步骤叫作剪枝

那么,怎么找到这个“先见之明”呢?经过我的总结,大概有以下几种情况:

  • 重复状态

例如:47. Permutations II 全排列 II(用重复数字) 这道题给定一个可包含重复数字的序列,要求返回所有不重复的全排列,例如输入[1,1,2]预期的输出为[1,1,2]、[1,2,1]、[2,1,1]。用我们前面介绍的解题模板,这道题并不难:

class Solution {
    fun permute(nums: IntArray): List<List<Int>> {
        val result = ArrayList<List<Int>>()

        // 选择列表
        val useds = BooleanArray(nums.size) { false }
        // 路径
        val track = L
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值