【算法】(Python)回溯算法

回溯算法:

  • 回溯算法是一种算法思想。
  • 采用“深度优先搜索(dfs,depth first search)”。
  • 采用“尝试”和“回溯”的策略。尝试搜索所有可能的解决方案,遇到不满足条件的撤销选择、回退到回溯点(满足回溯条件的某个状态的点)、尝试其他方法。
  • 基本思想:从一条路往前走,能进则进,不能进则退回来,换条路再试。
  • 类似穷举法,但有剪枝的功能。剪枝:去除不满足约束条件的尝试、提高效率。
  • 通常用递归实现。(也可以用迭代实现,但设计复杂)

回溯算法的伪代码模板:

res = []                                 # 记录所有的最终解

def traceback(各参数):

    if 终止条件:
        res.append(最终解)                # 保存结果
        return

    for 方案 in 所有可能的解决方案:          # 遍历所有可能的解决方案
        if 约束条件:                       # 剪枝
            做出选择,更新状态
            traceback(各参数)              # 递归搜索
            回溯,撤销选择,回退到上一状态


traceback(各参数)                          # 执行函数

举例:

(难度:简单)【力扣】1863. 找出所有子集的异或总和再求和

解题思路:搜索所有组合,深度优先搜索。遍历给定列表中每个元素,选择该元素并往下递归搜索,再撤销选择,尝试其他搜索。最后所有组合的异或结果求和。

知识点:x ^ y:x和y进行异或运算。异或运算:二进制中各位依次比对,相同为0,不同为1。 

class Solution:
    def subsetXORSum(self, nums: List[int]) -> int:
        
        def traceback(i, xor):
            # 终止条件:遍历到列表结束
            if i == n:
                res.append(xor)      # 最终解保存到结果列表中
                return
            
            # 选择当前元素
            traceback(i + 1, xor ^ nums[i])
            # 撤销选择(即不选择当前元素)
            traceback(i + 1, xor)

        res = []            # 结果列表,存放所有组合的异或结果
        xor = 0             # 一个组合的异或结果
        i = 0               # 目前是列表中哪个元素
        n = len(nums)       # 列表长度

        traceback(i, xor)   # 执行函数
        return sum(res)

(难度:中等)【力扣】46. 全排列

 解题思路:全排列:每个元素都有,只是顺序不同。因此,两个列表:结果列表(存放所有组合),存放一个全排列组合的列表。依次往组合中添加元素,给定列表中的每个元素只添加一次。添加元素时,每次从头遍历给定列表,分别选择该元素或不选择该元素。搜索所有组合。

知识点:res.append(alist[:]):往结果列表res中添加列表alist。需使用alist[:],否则回溯时pop撤销选择时,列表alist中会删除最后一个元素,结果列表res中也会删除该元素,最终结果则为空列表。

              alist.pop():删除列表alist中最后一个元素。

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        
        def traceback(index):
            # 终止条件:列表遍历结束
            if index == n:
                res.append(alist[:])        # 结果列表中保存组合
                return

            # 全排列(每个元素都有,只是顺序不同),每次从头遍历
            for i in range(0, n):
                # 每个元素在组合中只能出现一次
                if nums[i] not in alist:
                    # 选择当前元素
                    alist.append(nums[i])
                    traceback(index + 1)     # 递归,往下继续添加元素
                    # 撤销选择(即不选择当前元素)
                    alist.pop()

        res = []            # 结果列表,存放所有全排列的组合
        alist = []          # 一个全排列的组合
        index = 0           # 在组合中添加到哪个元素
        n = len(nums)       # 列表长度

        traceback(index)    # 执行函数
        return res

(难度:困难)【力扣】51. N 皇后

解题思路:皇后所在位置不能同一行、同一列、不能在两个斜线位置(左上到右下,右上到左下)。因此,三个集合:存放皇后所在列号,两个斜线(左上到右下,行号和列号的差相同。右上到左下,行号和列号的和相同)。搜索每一行皇后所在位置,尝试所有可能结果。

知识点: [ ]:空列表。列表:元素可重复的有序、可变的序列。

               列表.append(元素):往列表添加元素。

               列表[下标]:获取或修改列表下标对应的元素。

               "间隔符".join(列表):列表中所有元素按照指定间隔符组合成字符串。间隔符可以为空。

               [元素] * n:列表,列表中有n个相同的指定元素。

               set( ):空集合。集合:元素不重复的无序、可变的序列。

               集合.add(元素):往集合添加元素。

               集合.remove(元素):从集合删除元素。

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        def trackback(rowindex):
            # 终止条件:棋盘所有行遍历结束
            if rowindex == n:
                board = []                          # 棋盘列表,存放整个棋盘内容
                for i in range(n):
                    row[queen[i]] = "Q"             # 各行皇后所在的列下标对应元素为Q
                    board.append("".join(row))      # 各行加入棋盘
                    row[queen[i]] = "."             # 恢复行初始值
                res.append(board)                   # 结果列表记录最终整个棋盘
                return
            
            # 遍历棋盘所有列
            for colindex in range(n):
                # 判断:不与皇后同一列、斜线(左上到右下,行号与列号的差相同。右上到左下,行号与列号的和相同)
                if colindex not in columns \
                and colindex - rowindex not in diagonal_1 \
                and colindex + rowindex not in diagonal_2:
                    # 选择该位置为皇后
                    queen[rowindex] = colindex
                    columns.add(colindex)
                    diagonal_1.add(colindex - rowindex)
                    diagonal_2.add(colindex + rowindex)
                    trackback(rowindex + 1)            # 递归搜索
                    # 撤销选择(即该位置不为皇后)
                    columns.remove(colindex)
                    diagonal_1.remove(colindex - rowindex)
                    diagonal_2.remove(colindex + rowindex)
        
        res = []                # 结果列表,存放所有最终棋盘结果
        columns = set()         # 集合,存放所有皇后所在的列下标
        diagonal_1 = set()      # 集合,存放所有皇后的斜线(左上到右下)
        diagonal_2 = set()      # 集合,存放所有皇后的斜线(右上到左下)
        queen = [-1] * n        # 皇后列表,皇后初始值,存放每行皇后所在的列下标
        row = ['.'] * n         # 行列表,行初始值
        rowindex = 0            # 目前在哪一行

        trackback(rowindex)     # 执行函数
        return res

上一篇:014:无人机遥控器操作


下一篇:好用的idea插件之自动sql生成