回溯法数字全排列_什么是回溯算法(1)

a5f4f992035b6b647fc66afbbaef89bd.png

回溯算法顾名思义就是有回溯的算法呗~(当然这是句废话,只是缓解一下我第一次写知乎文章无从下笔的尴尬)。入正题 ↓

不知道看到这篇文章的小伙伴有没有看过一个之前大火的动漫《从零开始的异世界》,这部动漫里男主昂每次挂掉后都会从上个存档点开始新一轮的冒险,这就是一个简单的回溯思想——昂在不断的回溯中找到活下来的方法。回溯算法也具有同样的道理,往往来说,回溯算法先向下递归搜索一个可能解,然后通过不断地回溯到上一个“存档”在向下递归来找到所有满足条件的最优解。

其实,回溯思想有点类似于for循环暴力遍历所有解空间,唯一不同的地方是回溯算法往往使用递归来实现。而递归相比于for循环具有一个先天的优势,就是无需知道遍历的深度,所以能用for循环实现的一定能用递归实现,但是反过来可不一定成立哦~

多说无意,举个简单的例子,以方便直观的理解。

比如,经典的全排列问题(题目来自leetcode):

# 给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
 输入: [1,2,3]
 输出:
 [
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
 ]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations

咋一看这道题好像可以用for循环暴力解,但是如果你真的尝试使用for循环去实现全排列的话,会发现真的很难写(总会缺斤少两 ~),原因是遍历的深度会随着集合长度增加而增加。但是如果用回溯思想则可以很简单的实现。为了先构建起回溯算法的基本思路,这里以上面的示例为例,我们看看回溯算法是如何进行搜索的。

434f3d0d9b00b73d5abd72527c62f670.png
全排列回溯算法图解-纯手工绘图(不喜勿喷)

如上图所示,我们可以把全排列问题分为3个小部分:

  • 以1为开头的所有排列 = [1] + [2,3]的全排列
  • 以2为开头的所有排列 = [2] + [1,3]的全排列
  • 以3为开头的所有排列 = [3] + [1,2]的全排列

每次向下递归都是在尚未访问元素中继续进行,如果递归触底则向上回溯到最近的一个元素接着向下递归。下面是Python写的全排列代码,读者可以参考。

class Solution(object):
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        length = len(nums)
        res = []
        # checked 数组用来回溯
        def permute_dfs(numset , checked, deepth):
            if deepth == length:
                res.append(checked[:])
                return
            for i in range(length):
                if numset[i] not in checked:
                    checked.append(numset[i])       # 先向下递归
                    permute_dfs(numset, checked , deepth+1)
                    checked.pop()                   # 回溯到上一个节点

        permute_dfs(nums , [] , 0)
        return res

这里回溯的实现是通过checked数组+递归来实现的,我们在递归中用checked数组记录访问的路径,每当节点的其中一个分支递归结束后,就把这个分支的路径清除,以便继续另一个分支,这就是回溯的思想。

leetcode上还有一些和全排列问题非常相似的回溯问题,如果这里的思想看明白了的话,推荐尝试做一做下面这些同类型的题目,以加固对回溯算法的理解。

全排列II​leetcode-cn.com 组合​leetcode-cn.com 子集​leetcode-cn.com

回溯算法除了求解类似全排列这种整个解空间的问题之外,其实更多的场景是用在求最优解的问题上,通过暴力回溯遍历整个解空间以寻求我们需要的最优解。所以单从回溯过程来看,回溯算法并没有很高的效率,只是为我们解决问题提供了一种通用的解法。寻找最优解的问题往往是在回溯算法框架之上,加入了剪枝函数,通过提前把不满足条件的分支剪掉来减少搜索的次数,进而提升回溯算法的速度。

寻找最优解空间的回溯问题,放到下篇接着分享吧,这篇就到这里~。第一次写文章,希望大家多支持,有问题的地方可以戳评论区,感谢批评指正!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值