回溯算法的原理和实现步骤

1.原理

回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。

在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。

2.操作

也就是说解决一个回溯问题,实际上就是一个决策树的遍历过程。在这个过程中只需要思考三个问题:
(1)路径:也就是已经做出的选择;
(2)选择列表:也就是你当前可以做的选择;
(3)结束条件:也就是1到达决策树底层,无法再做选择的条件
回溯算法框架:

result = []
def backtrack(路径, 选择列表):
	if 满足结束条件:
		result.add(路径)
		return
	
	for 选择 in 选择列表:
		做选择
		backtrack(路径, 选择列表)
		撤销选择

核心是for循环里面的递归,在递归调用之前做选择,在递归调用之后撤销选择。

3.例子

例一:

Leetcode算法题库里面的39题:
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
在这里插入图片描述

代码:

class Solution:
    def combinationSum(self, candidates, target):
        size = len(candidates)
        candidates.sort()
        print(candidates[0])
        res = []
        path = []
        self.backtrack(res, candidates, target, path, size)
        return res


    def backtrack(self, res, candidates, target, path, size):

        if target == 0:
            x = path[:]
            x.sort()
            if x not in res:
                res.append(path[:])
            return
        for index in range(0, size):
            residue = target - candidates[index]
            if residue < 0:  # 剪枝操作,优化代码
                break
            path.append(candidates[index])
            self.backtrack(res, candidates, residue, path, size)
            path.pop()


if __name__ == '__main__':
    candidates = [2, 3, 6, 7]
    target = 7
    solution = Solution()
    result = solution.combinationSum(candidates, target)
    print(result)

结果:

[[2, 2, 3], [7]]

其中路径是path,选择列表是candidates,结束条件是 if target == 0,做选择用了一个append,撤销选择用了一个出栈的操作。因为candidates中的数据可以无限重复选取,所以每次递归的选择列表都不变。

例二:

Leetcode算法题库里面的46题
在这里插入图片描述
代码:

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def backtrack(path, nums):
        	# 如果满足满足结束条件则添加路径
            if len(path) == n:
                path_s = path.copy()
                result.append(path_s)
                return
            for i in range(0, n):
                if nums[i] not in path: #每个数字只能用一次
                	# 路径选择
                    path.append(nums[i])
                    # 回溯
                    backtrack(path, nums)
                    # 撤销路径
                    path.pop()

        result = []
        n = len(nums)
        path = []
        backtrack(path, nums)
        return result

例三:

[Leetcode算法题库里面的47题
在这里插入图片描述代码:

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def backtrack():
        	# 如果满足满足结束条件则添加路径
            if len(path) == n and path not in result:
                path_s = path.copy()
                result.append(path_s)
                return
            for i in range(0, n):
            	# 路径选择
            	# 设置index可以保证不做重复选择
                if i not in index:
                    index.append(i)
                    path.append(nums[i])
                else:
                    continue
                # 回溯
                backtrack()
                # 撤销选择
                path.pop()
                index.pop()

        result = []
        n = len(nums)
        index = []
        path = []
        backtrack()
        return result

参考labuladong的博文
Leetcode上的代码

例四:

def subsets(nums):
    def backtrack(first, path):
        if len(path) == k:
            path_s = path.copy()
            result.append(path_s)
            return

        for i in range(first, n):
            if nums[i] not in path:
                # 是否要对第i+1个item进行回溯,前面的i个item已经判断完了,不用管
                path.append(nums[i])
                backtrack(i+1, path)
                path.pop()

    n = len(nums)
    path = []
    result = []
    # 表示从nums中取k个item
    for k in range(n+1):
        backtrack(0, path)
    return result

nums = [1,2,3]
print(subsets(nums))

leetcode 78:子集

例五

"practice_17.py" 28L, 791C
def letterCombinations(digits):
    def backtrack(index, path):
        if n == 0:
            return []
        if len(path) == n:
            result.append(''.join(path[:]))
            return

        for letter in digit2letter_dict[digits[index]]:
            path.append(letter)
            backtrack(index+1, path)
            path.pop()

    digit2letter_dict = {'2':['a', 'b', 'c'], '3':['d', 'e', 'f'],
                         '4':['g', 'h', 'i'], '5':['j', 'k', 'l'],
                         '6':['m', 'n', 'o'], '7':['p', 'q', 'r', 's'],
                         '8':['t', 'u', 'v'], '9':['w', 'x', 'y', 'z']}
    n = len(digits)
    path = []
    result = []
    backtrack(0, path)
    return result


digits = "23"
# digits = "2"
digits = ""
print(letterCombinations(digits))

leetcode的第17题

例六:二维平面上使用回溯

Leetcode算法题库里面的79题

剑指offer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

comli_cn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值