回溯算法leetcode题讲解

回溯

回溯算法是一种通过探索所有可能的候选解来找出所有解的算法。如果候选解被确认不是一个解的话(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化来舍弃该解,即“回溯”。

以下是回溯法的一般解题模板:

  1. 定义问题的解空间:确定问题的所有可能解的范围,这个范围被称为解空间。

  2. 确定解空间的组织结构:解空间通常是一个树或图的结构。对于每个节点,确定其子节点的生成规则。

  3. 搜索解空间:从根节点开始,按照生成规则逐层向下搜索,直到找到满足条件的解。

  4. 回溯:当搜索到某一层时,发现当前节点不能得到满足条件的解,就回退到上一层,继续搜索。

  5. 记录结果:在搜索过程中,将满足条件的解记录下来。

子集

class Solution():
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        self.backtrack(result, [], nums, 0)
        return result

    def backtrack(self, result, temp, nums, start):
        # 将当前组合添加到结果列表中
        result.append(temp[:])
        # 从start开始遍历数组
        for i in range(start, len(nums)):
            # 将当前元素添加到临时列表中
            temp.append(nums[i])
            # 递归调用回溯函数,继续寻找下一个元素
            self.backtrack(result, temp, nums, i + 1)
            # 回溯,移除最后一个元素
            temp.pop()
 

组合

class Solution():
    def combine(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: List[List[int]]
        """
        result = []
        self.backtrack(result, [], n, k, 1)
        return result

    def backtrack(self, result, temp, n, k, start):
        # 如果临时列表的长度等于k,将其添加到结果列表中
        if len(temp) == k:
            result.append(temp[:])
            return
        # 从start开始遍历到n
        for i in range(start, n + 1):
            # 将当前元素添加到临时列表中
            temp.append(i)
            # 递归调用回溯函数,继续寻找下一个元素
            self.backtrack(result, temp, n, k, i + 1)
            # 回溯,移除最后一个元素
            temp.pop()
 

排列

'''
排列
'''
class Solution():
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        self.backtrack(result, [], nums)
        return result

    def backtrack(self, result, temp, nums):
        # 如果临时列表的长度等于nums的长度,将其添加到结果列表中
        if len(temp) == len(nums):
            result.append(temp[:])
            return
        # 遍历nums中的每个元素
        for i in range(len(nums)):
            # 如果该元素已经在临时列表中,跳过
            if nums[i] in temp:
                continue
            # 将当前元素添加到临时列表中
            temp.append(nums[i])
            # 递归调用回溯函数,继续寻找下一个元素
            self.backtrack(result, temp, nums)
            # 回溯,移除最后一个元素
            temp.pop()

全排列

'''
全排列
'''
class Solution():
    def permuteUnique(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        self.used= False*len(nums)
        nums.sort()  # 先对数组进行排序,保证字典序递增
        self.backtrack(result, [], nums)
        return result

    def backtrack(self, result, temp, nums):
        # 如果临时列表的长度等于nums的长度,将其添加到结果列表中
        if len(temp) == len(nums):
            result.append(temp[:])
            return
        # 遍历nums中的每个元素
        for i in range(len(nums)):
            # 如果该元素已经在临时列表中,或者与前一个元素相同且前一个元素还未使用过,跳过
            if i > 0 and nums[i] == nums[i - 1] and not self.used[i - 1]:
                continue
            # 如果该元素已经使用过,跳过
            if self.used[i]:
                continue
            # 标记该元素为已使用
            self.used[i] = True
            # 将当前元素添加到临时列表中
            temp.append(nums[i])
            # 递归调用回溯函数,继续寻找下一个元素
            self.backtrack(result, temp, nums)
            # 回溯,移除最后一个元素并标记为未使用
            temp.pop()
            self.used[i] = False

组合总数

'''
组合总数
'''
class Solution:
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        result = []
        candidates.sort()  # 先对数组进行排序,方便剪枝操作
        self.backtrack(result, [], candidates, target, 0)
        return result

    def backtrack(self, result, temp, candidates, target, start):
        # 如果临时列表的元素之和等于target,将其添加到结果列表中
        if sum(temp) == target:
            result.append(temp[:])
            return
        # 如果临时列表的元素之和大于target,直接返回,不再继续搜索
        if sum(temp) > target:
            return
        # 从start开始遍历candidates中的每个元素
        for i in range(start, len(candidates)):
            # 将当前元素添加到临时列表中
            temp.append(candidates[i])
            # 递归调用回溯函数,继续寻找下一个元素
            self.backtrack(result, temp, candidates, target, i)
            # 回溯,移除最后一个元素
            temp.pop()

组合总数2



'''
组合总数2
'''
class Solution:
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        result = []
        candidates.sort()  # 先对数组进行排序,方便剪枝操作和去重操作
        self.backtrack(result, [], candidates, target, 0)
        return result

    def backtrack(self, result, temp, candidates, target, start):
        # 如果临时列表的元素之和等于target,将其添加到结果列表中
        if sum(temp) == target:
            result.append(temp[:])
            return
        # 如果临时列表的元素之和大于target,直接返回,不再继续搜索
        if sum(temp) > target:
            return
        # 从start开始遍历candidates中的每个元素,注意避免重复组合的出现
        for i in range(start, len(candidates)):
            # 如果当前元素与前一个元素相同,并且前一个元素还未使用过,跳过本次循环,避免重复组合的出现
            if i > start and candidates[i] == candidates[i - 1]:
                continue
            # 将当前元素添加到临时列表中
            temp.append(candidates[i])
            # 递归调用回溯函数,继续寻找下一个元素
            self.backtrack(result, temp, candidates, target, i + 1)
            # 回溯,移除最后一个元素
            temp.pop()

解数独

def is_valid(board, row, col, num):
    # 检查行是否包含数字
    for i in range(9):
        if board[row][i] == num:
            return False

    # 检查列是否包含数字
    for i in range(9):
        if board[i][col] == num:
            return False

    # 检查3x3方格是否包含数字
    start_row = row - row % 3
    start_col = col - col % 3
    for i in range(3):
        for j in range(3):
            if board[i + start_row][j + start_col] == num:
                return False

    return True

def solve_sudoku(board):
    empty_cell = find_empty_cell(board)
    if not empty_cell:
        return True

    row, col = empty_cell

    for num in range(1, 10):
        #当前位置只能判断和之前填过的有没有冲突,后续填了有冲突会撤销
        if is_valid(board, row, col, num):
            board[row][col] = num
            if solve_sudoku(board):
                return True
            #回溯
            board[row][col] = 0
    return False

def find_empty_cell(board):
    for i in range(len(board)):
        for j in range(len(board[0])):
            if board[i][j] == 0:
                return (i, j)
    return None

# 示例数独
board = [
    [5, 3, 0, 0, 7, 0, 0, 0, 0],
    [6, 0, 0, 1, 9, 5, 0, 0, 0],
    [0, 9, 8, 0, 0, 0, 0, 6, 0],
    [8, 0, 0, 0, 6, 0, 0, 0, 3],
    [4, 0, 0, 8, 0, 3, 0, 0, 1],
    [7, 0, 0, 0, 2, 0, 0, 0, 6],
    [0, 6, 0, 0, 0, 0, 2, 8, 0],
    [0, 0, 0, 4, 1, 9, 0, 0, 5],
    [0, 0, 0, 0, 8, 0, 0, 7, 9]
]

if solve_sudoku(board):
    for row in board:
        print(row)
else:
    print("无解")

n皇后

def solveNQueens(n):
    def is_valid(board, row, col):
        # 检查同一列是否有其他皇后
        for i in range(row):
            if board[i][col] == 'Q':
                return False
        # 检查左上对角线是否有其他皇后
        i, j = row - 1, col - 1
        while i >= 0 and j >= 0:
            if board[i][j] == 'Q':
                return False
            i -= 1
            j -= 1
        # 检查右上对角线是否有其他皇后
        i, j = row - 1, col + 1
        while i >= 0 and j < n:
            if board[i][j] == 'Q':
                return False
            i -= 1
            j += 1
        return True

    def backtrack(board, row):
        #边界
        if row == n:
            result.append([''.join(row) for row in board])
            return
        for col in range(n):
            #在row col添加q是否有效
            if not is_valid(board, row, col):
                continue
            board[row][col] = 'Q'
            #回溯
            backtrack(board, row + 1)
            #撤销 彻底结束到n,或者中间有失败的就会for循环结束就会返回上一层,当前状态的后续状态都尝试完了
            board[row][col] = '.'

    result = []
    board = [['.' for _ in range(n)] for _ in range(n)]
    backtrack(board, 0)
    return result

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

茉莉_Molly

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

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

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

打赏作者

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

抵扣说明:

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

余额充值