Bootstrap

华为OD机试专栏--1.3 算法基础:1.3.4 回溯算法与递归思想

目录

1.3 算法基础

1.3.4 回溯算法与递归思想

一、回溯算法的核心思想

1.1 什么是回溯算法?

1.2 回溯算法的特点

二、递归思想

2.1 什么是递归?

三、回溯算法的经典问题

3.1 全排列问题

问题描述

回溯解法

代码实现(Python)

3.2 子集问题

问题描述

回溯解法

代码实现(Python)

3.3 N皇后问题

问题描述

回溯解法

代码实现(Python)

3.4 组合总和问题

问题描述

回溯解法

代码实现(Python)

四、总结


1.3 算法基础

1.3.4 回溯算法与递归思想

回溯算法是一种系统化的试探方法,广泛应用于解决组合优化、搜索和约束满足等问题。它通过递归的方式逐步构建解空间树,并在遇到不满足条件的情况时撤销选择(即“回溯”)。本节将详细介绍回溯算法的基本原理、递归思想以及经典应用场景。


一、回溯算法的核心思想

1.1 什么是回溯算法?

回溯算法是一种深度优先搜索(DFS)的改进版,适用于求解具有明确约束条件的问题。其核心思想是:

  • 构建解空间树,从根节点开始逐步扩展可能的解。
  • 在每个节点上检查当前状态是否满足约束条件。
  • 如果满足,则继续扩展;如果不满足,则撤销当前选择并返回上一层(即回溯)。

回溯算法通常用于以下场景:

  • 排列组合问题(如全排列、子集问题)。
  • 搜索问题(如迷宫问题、八皇后问题)。
  • 约束满足问题(如数独问题)。

1.2 回溯算法的特点

  1. 递归实现:回溯算法通常通过递归函数实现,递归栈用来记录当前的状态路径。
  2. 剪枝优化:在搜索过程中,可以通过提前判断某些分支是否可行来减少不必要的计算。
  3. 穷举性:回溯算法会尝试所有可能的解,直到找到满足条件的结果或遍历完整个解空间。

二、递归思想

2.1 什么是递归?

递归是一种函数调用自身的编程技巧。递归算法通常由两部分组成:

  1. 基准条件(Base Case):递归终止的条件,避免无限递归。
  2. 递归步骤(Recursive Step):将问题分解为更小的子问题,并通过递归调用解决这些子问题。

递归的核心在于将复杂问题分解为简单问题,并利用已有结果构造最终答案。


三、回溯算法的经典问题

3.1 全排列问题

问题描述

给定一个不含重复数字的数组 numsnums,返回所有可能的排列。

回溯解法
  • 状态定义:当前已选择的路径和剩余未选择的元素集合。
  • 递归过程:每次从剩余元素中选择一个加入路径,并递归处理剩余元素。
  • 回溯操作:当完成一次递归后,撤销当前选择以尝试其他可能性。
代码实现(Python)
 

Python

深色版本

def permute(nums):
    def backtrack(path, remaining):
        if not remaining:
            result.append(path[:])
            return
        for i in range(len(remaining)):
            # 选择
            path.append(remaining[i])
            # 递归
            backtrack(path, remaining[:i] + remaining[i+1:])
            # 撤销选择
            path.pop()
    
    result = []
    backtrack([], nums)
    return result

# 测试用例
nums = [1, 2, 3]
print(permute(nums))  # 输出:[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

3.2 子集问题

问题描述

给定一个整数数组 numsnums,返回其所有可能的子集(幂集)。

回溯解法
  • 状态定义:当前已选择的路径和剩余未选择的元素集合。
  • 递归过程:每次可以选择当前元素或跳过当前元素,分别递归处理两种情况。
  • 回溯操作:无需显式撤销选择,因为每种选择都会生成独立的路径。
代码实现(Python)
 

Python

深色版本

def subsets(nums):
    def backtrack(start, path):
        result.append(path[:])  # 当前路径加入结果
        for i in range(start, len(nums)):
            path.append(nums[i])  # 选择
            backtrack(i + 1, path)  # 递归
            path.pop()  # 撤销选择
    
    result = []
    backtrack(0, [])
    return result

# 测试用例
nums = [1, 2, 3]
print(subsets(nums))  # 输出:[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]

3.3 N皇后问题

问题描述

在一个 N \times NN×N 的棋盘上放置 NN 个皇后,使得它们互不攻击(即不在同一行、同一列或同一对角线)。求所有可能的摆放方案。

回溯解法
  • 状态定义:当前已放置皇后的行号及其对应的列号。
  • 递归过程:逐行尝试放置皇后,检查当前位置是否与已放置的皇后冲突。
  • 回溯操作:如果当前行无法放置皇后,则撤销上一行的选择并尝试其他列。
代码实现(Python)
 

Python

深色版本

def solveNQueens(n):
    def is_safe(row, col):
        for r, c in queens:
            if c == col or abs(r - row) == abs(c - col):
                return False
        return True

    def backtrack(row):
        if row == n:
            result.append(["".join(["Q" if j == queens[i][1] else "." for j in range(n)]) for i in range(n)])
            return
        for col in range(n):
            if is_safe(row, col):
                queens.append((row, col))
                backtrack(row + 1)
                queens.pop()

    result = []
    queens = []
    backtrack(0)
    return result

# 测试用例
n = 4
solutions = solveNQueens(n)
for solution in solutions:
    print(solution)

输出示例:

 

Plaintext

深色版本

['.Q..', '...Q', 'Q...', '..Q.']
['..Q.', 'Q...', '...Q', '.Q..']

3.4 组合总和问题

问题描述

给定一个无重复元素的正整数数组 candidatescandidates 和目标值 targettarget,找出所有可以使数字和为目标值的组合。每个数字可以使用任意次。

回溯解法
  • 状态定义:当前已选择的路径和剩余目标值。
  • 递归过程:每次从候选数组中选择一个数字加入路径,并递归处理剩余目标值。
  • 回溯操作:当目标值为零时,记录当前路径;否则撤销选择以尝试其他可能性。
代码实现(Python)
 

Python

深色版本

def combinationSum(candidates, target):
    def backtrack(start, path, remain):
        if remain == 0:
            result.append(path[:])
            return
        for i in range(start, len(candidates)):
            if candidates[i] > remain:
                continue
            path.append(candidates[i])
            backtrack(i, path, remain - candidates[i])
            path.pop()
    
    result = []
    backtrack(0, [], target)
    return result

# 测试用例
candidates = [2, 3, 6, 7]
target = 7
print(combinationSum(candidates, target))  # 输出:[[2, 2, 3], [7]]

四、总结

本节详细介绍了回溯算法的基本原理、递归思想以及经典应用场景。回溯算法的核心在于通过递归构建解空间树,并通过剪枝优化搜索过程。掌握这些技巧后,可以高效地解决许多复杂的组合优化和搜索问题。

下一节,我们将深入探讨分治算法及其应用,敬请期待!

如果你觉得本文对你有所帮助,请点赞支持并关注我的CSDN博客,我们将持续输出更多优质内容!

;