回溯法

摘自Backtracking回溯法(又称DFS,递归)全解,文章写得很不错!!!

回溯是啥

用爬山来比喻回溯,好比从山脚下找一条爬上山顶的路,起初有好几条道可走,当选择一条道走到某处时,又有几条岔道可供选择,只能选择其中一条道往前走,若能这样子顺利爬上山顶则罢了,否则走到一条绝路上时,只好返回到最近的一个路口,重新选择另一条没走过的道往前走。如果该路口的所有路都走不通,只得从该路口继续回返。照此规则走下去,要么找到一条到达山顶的路,要么最终试过所有可能的道,无法到达山顶。
回溯是一种穷举,但与brute force有一些区别,回溯带了两点脑子的,并不多,brute force一点也没带。
第一点脑子是回溯知道回头;相反如果是brute force,发现走不通立刻跳下山摔死,换第二条命从头换一条路走。
第二点脑子是回溯知道剪枝;如果有一条岔路上放了一坨屎,那这条路我们不走,就可以少走很多不必要走的路。

那如何使用回溯法解决问题呢,一般选用递归实现;当然也可以不用,用栈(例如,可以用递归的方式实现二叉树的遍历,也可以用栈的方式实现遍历)。

递归该如何理解?

  • 递归的目的是将大问题化成小问题(根据递归函数的参数来决定规模),一旦小问题解决之后,再由里到外依次解决问题
  • 递归本质上就是一个函数,我们可以将它视为一个黑夹子,要么返回结果,要么对传入的参数进行改变,无需进入递归函数里去思考
  • 每一个递归函数都有一个终止条件

回溯法一般用来解决哪类问题呢?

  • 排列
  • 组合
  • 子集

解决上面三类问题都遵循着特定的套路,请看下面的例子

排列问题

leetcode46:全排列

给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:

输入: [1,2,3]
输出:[ [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1] ]
class Solution:
    def permute(self, nums):
        if len(nums) == 0:
            return []
        self.res = []
        temp = []
        l = len(nums)
        self.helper(l,nums, temp)
        return self.res

    def helper(self, l,nums,temp):
        if len(temp) == l:
            c = temp[:]
            self.res.append(c)
            return
        for i in range(len(nums)):
            temp.append(nums[i])
            self.helper(l,nums[:i]+nums[i+1:],temp)
            temp.pop()

leetcode47:全排列 II

给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:

输入: [1,1,2]
 输出:[ [1,1,2],[1,2,1],[2,1,1]]
class Solution:
    def permute(self, nums):
        if len(nums) == 0:
            return []
        self.res = []
        temp = []
        nums = sorted(nums)
        l = len(nums)
        self.helper(l,nums, temp)
        return self.res

    def helper(self, l,nums,temp):
        if len(temp) == l:
            c = temp[:]
            self.res.append(c)
            return

        n = None 

        for i in range(len(nums)):
            if n == nums[i]: # 剪枝,用来判断是否有重复操作
                continue

            temp.append(nums[i])
            t = nums[:i]+nums[i+1:]
            self.helper(l,nums[:i]+nums[i+1:],temp)
            n = temp.pop()

排列出二维数组的所有可能性

输入[[1,2,4],[3,5],[7]]
输出[[1,3,7],[1,5,7],[2,3,7],[2,5,7],[4,3,7],[4,5,7]]

class Solution(object):
    def listall(self, nums:list) -> list:
        self.res = []
        self.helper(0,nums, [])
        return self.res

    def helper(self, start, nums, temp):

        if len(temp) == len(nums):
            self.res.append(temp[:])
            return

        for i in range(start,len(nums)):
            for j in range(len(nums[i])):
                temp.append(nums[i][j])
                self.helper(i+1,nums,temp)
                temp.pop()


if __name__ == '__main__':
    print(Solution().listall([[1,2,4],[3,5],[7]]))

组合问题

leetcode77:组合

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:

输入: n = 4, k = 2
输出:[ [2,4],[3,4],[2,3],[1,2],[1,3],[1,4],]
class Solution(object):
    def combine(self, n, k):
        if n < k:return []
        self.res = []
        self.helper(n,k,1,[])
        return self.res


    def helper(self,n,k,start,temp):
        if len(temp) == k:
            c = temp[:]
            self.res.append(c)
            return

        for i in range(start,n+1):

            temp.append(i)
            self.helper(n,k,i+1,res,temp)
            temp.pop()

leetcode39:组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。
    示例
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]
class Solution(object):
    def combinationSum(self,candidates, target):
        self.res = []
        self.helper(0,candidates,target,[])
        return self.res

    def helper(self,start,candidates,target,temp):
        if sum(temp) == target:
            c = temp[:]
            self.res.append(c)
            return
        if sum(temp) > target:
            return

        for i in range(start,len(candidates)):

            temp.append(candidates[i])
            self.helper(i,candidates,target,temp)
            temp.pop()

leetcode40:组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]
class Solution(object):
    def combinationSum2(self,candidates, target):
        self.res = []
        candidates = sorted(candidates)
        self.helper(0,candidates,target,[])
        return self.res

    def helper(self,start,candidates,target,temp):
        if sum(temp) == target:
            c = temp[:]
            self.res.append(c)
            return
        if sum(temp) > target:
            return

        n = None
        for i in range(start,len(candidates)):
            if n == candidates[i]:
                continue
            temp.append(candidates[i])
            self.helper(i+1,candidates,target,temp)
            n = temp.pop()

leetcode216:组合总和 III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。
    示例:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
class Solution:
    def combinationSum3(self, k, n):
        """
        :type k: int
        :type n: int
        :rtype: List[List[int]]
        """
        self.res = []
        if n < 1 or k < 1: return self.res
        self.helper(k,n,1,[])
        return self.res

    def helper(self,k,n,start,temp):
        if len(temp) > k :
            return

        if len(temp) == k and sum(temp) == n:
            c= temp[:]
            self.res.append(c)
            return

        for i in range(start,10):
            temp.append(i)
            self.helper(k,n,i+1,temp)
            temp.pop()

子集问题

leetcode78:子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:

输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
class Solution:
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        self.res = []
        if len(nums)<1: return self.res
        self.helper(nums,0,[])
        self.res.append([])
        return self.res


    def helper(self,nums,start,temp):

        for i in range(start,len(nums)):
            temp.append(nums[i])
            c = temp[:]
            self.res.append(c)
            self.helper(nums,i+1,temp)
            temp.pop()

补充

剑指offer25:二叉树中和为某一值的所有路径,其中路径定义是从根结点到叶子结点所形成的路径

class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        if root is None: return []
        self.res = []
        self.helper(root, expectNumber, [])
        return self.res


    def helper(self, root, expectNumber, temp):
        temp.append(root)
        xsum = sum(map(lambda x: x.val, temp))
        if  xsum == expectNumber and root.left is None and root.right is None:
            self.res.append(list(map(lambda x: x.val, temp[:])))
            return
        if xsum > expectNumber:
            return
        if root.left is not None:
            self.helper(root.left, expectNumber, temp)
            temp.pop()
        if root.right is not None:
            self.helper(root.right, expectNumber, temp)
            temp.pop()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 一、基本概念   回溯法,又称为试探法,按选优条件向前不断搜索,以达到目标。但是当探索到某一步时,如果发现原先选择...
    大雄的学习人生阅读 1,161评论 1 3
  • 回溯法动态规划题目https://www.cnblogs.com/jiangchen/p/5930049.html...
    mrjunwang阅读 253评论 0 0
  • 1.支持 MYSQL锁 官方文档 首先需要明确的是,对于MySQL而言,不同的engine所支持的锁类型不一样:M...
    Separes阅读 1,047评论 0 3
  • 阔瞰无余北海巅。惯云舒雾浅,气神闲。空晴幽壑遇狂澜,枝零乱,骤雨惹芳嫣。 百瀑破叠帘,千峰突累幔,万松援。祥猴瑞现...
    文泳阅读 178评论 0 4
  • 我要把这该死的浪漫丢弃 受够了被人孤立 尝遍了孤独无依 我要学会花言巧语 与世俗打成一片 再不为风花雪月献上赞礼 ...
    苏家富贵儿阅读 373评论 4 4