Leetcode 回溯 3

题号直达: 79 单词搜索 | 131 分割回文串 | 51 N 皇后

LeetCode 79. 单词搜索 ↑

题目

给定一个 m × n 的字符网格 board 和一个字符串 word。若 word 可由网格中若干格子中的字母按顺序拼出,则返回 true,否则返回 false。

规则: 在网格中依次选相邻格子(上下左右四连通);同一格子在同一条搜索路径中只能使用一次。

示例图

题目示例示意(0407_2.png`):

单词搜索 题意与示例

提示

  • m == len(board),n == len(board[i])
  • 1 ≤ m, n ≤ 6
  • 1 ≤ len(word) ≤ 15
  • board 与 word 仅由大小写英文字母组成

代码(Python):DFS + 访问标记回溯

从每个格子 (i, j) 尝试作为起点,当前需匹配 word[k]。字符一致则标记 visited[i][j] = True,向四邻未访问格子递归 k + 1;任一分支成功即成功;回溯前 visited[i][j] = False 撤销标记。

class Solution(object):
    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        visited = [
            [False for _ in range(len(board[0]))] for _ in range(len(board))
        ]

        def check(i, j, k):
            if board[i][j] != word[k]:
                return False
            if k == len(word) - 1:
                return True
            visited[i][j] = True
            test = []

            if i > 0 and not visited[i - 1][j]:
                test.append([i - 1, j])
            if j > 0 and not visited[i][j - 1]:
                test.append([i, j - 1])
            if i < len(board) - 1 and not visited[i + 1][j]:
                test.append([i + 1, j])
            if j < len(board[0]) - 1 and not visited[i][j + 1]:
                test.append([i, j + 1])

            judge = False
            for m in range(len(test)):
                judge = judge or check(test[m][0], test[m][1], k + 1)
                if judge:
                    break
            visited[i][j] = False
            return judge

        for i in range(len(board)):
            for j in range(len(board[0])):
                if check(i, j, 0):
                    return True
        return False

思路解析

  • 起点枚举:单词首字母可能与多个格匹配,需对每个 (i, j) 尝试 check(i, j, 0)。
  • 剪枝:当前格字符不等于 word[k] 时直接返回 false;k == len(word) - 1 且字符已匹配则整条路径成功。
  • 回溯:visited 防止同一路径重复踩格;递归返回后必须恢复为 False,以便其他起点或分支复用该格。
  • 复杂度:网格规模小(至多 6×6),单词至多 15;最坏搜索与路径数相关,在题目约束下可接受。

LeetCode 131. 分割回文串 ↑

题目

给定字符串 s,将 s 分割成若干子串,使每个子串都是回文串。返回所有可能的分割方案。

测试样例

  • 示例 1: s = "aab"
    输出:[["a","a","b"],["aa","b"]]

  • 示例 2: s = "a"
    输出:[["a"]]

提示

  • 1 ≤ len(s) ≤ 16
  • s 仅由小写英文字母组成

代码(Python):DP 预处理回文 + DFS 回溯

先用二维数组 f[i][j] 表示 s[i:j+1] 是否为回文:s[i]==s[j] 且中间 s[i+1:j-1] 为回文(i 从大到小填表)。DFS 从位置 i 出发,枚举结束位置 j,若 f[i][j] 为真则把 s[i:j+1] 加入当前方案并递归 dfs(j+1);返回前 pop 回溯。

class Solution(object):
    def partition(self, s):
        """
        :type s: str
        :rtype: List[List[str]]
        """
        n = len(s)
        f = [[True] * n for _ in range(n)]

        for i in range(n - 1, -1, -1):
            for j in range(i + 1, n):
                f[i][j] = (s[i] == s[j]) and f[i + 1][j - 1]

        ret = []
        ans = []

        def dfs(i):
            if i == n:
                ret.append(ans[:])
                return
            for j in range(i, n):
                if f[i][j]:
                    ans.append(s[i : j + 1])
                    dfs(j + 1)
                    ans.pop()

        dfs(0)
        return ret

思路解析

  • 回文 DP:f[i][j] 为真当且仅当两端字符相同且内侧子串(f[i+1][j-1])为回文;i 从 n-1 递减,保证内层已算好。
  • 分割 DFS:在 i 处枚举第一段结束于 j,仅当 f[i][j] 为真才切分;递归处理剩余部分 j+1;i==n 时复制一份 ans 加入 ret。
  • 复杂度:DP O(n^2);DFS 与回文分割方案数相关,最坏指数级,但 n ≤ 16 可接受。

LeetCode 51. N 皇后 ↑

题目

困难。 国际象棋中,皇后可以攻击同一行、同一列、同一斜线上的棋子。n 皇后问题:在 n × n 的棋盘上放置 n 个皇后,使它们互不攻击。

给定整数 n,返回所有不同的解决方案。每种方案用字符串棋盘表示:'Q' 表示皇后,'.' 表示空位。

示例图

题目示例示意(0408.png`):

N 皇后 题意与示例

提示

  • 1 ≤ n ≤ 9

代码(Python)解法一:回溯 + 列与斜线占用

按行递归:第 i 行尝试列 j。用列表记录已占用的列、主对角线(j - i)、副对角线(j + i),若均未冲突则放置并递归下一行;回溯时同步 pop。t 存每行皇后所在列,凑满 n 行后得到一种列向量,再经 transform 转成 n 条字符串。

class Solution(object):
    def transform(self, info):
        ans = []
        for i in range(len(info)):
            row = ["."] * len(info)
            row[info[i]] = "Q"
            row = "".join(row)
            ans.append(row)
        return ans

    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        columns = []
        diagonals1 = []
        diagonals2 = []
        t = []
        ans = []

        def check(i):
            if i == n:
                ans.append(t[:])
            else:
                for j in range(n):
                    if (
                        j not in columns
                        and j + i not in diagonals2
                        and j - i not in diagonals1
                    ):
                        t.append(j)
                        columns.append(j)
                        diagonals1.append(j - i)
                        diagonals2.append(j + i)
                        check(i + 1)
                        t.pop()
                        columns.pop()
                        diagonals1.pop()
                        diagonals2.pop()

        check(0)
        for N in range(len(ans)):
            ans[N] = self.transform(ans[N])
        return ans

思路解析(解法一)

  • 一行一后:每行恰放一个皇后,递归深度为 n;列与两条斜线不重复即可覆盖「互不攻击」。
  • 斜线编号:同一主对角线上 j - i 不变,同一副对角线上 j + i 不变(与行 i、列 j 对应)。
  • 输出:t 为长度为 n 的列下标序列,transform 逐行把 Q 放在对应列,其余为 '.'。
  • 复杂度:搜索树在 n ≤ 9 内可接受;可用 set 代替列表判重以常数优化。

代码(Python)解法二:回溯 + 每行可用列布尔表

仍在第 i 行决策,但不用全局 columns / 对角线集合:每进入一行先建 bin_record 长为 n,初值全为可放。对已放置的上一行皇后 (j, t[j]),令 diff = i - j(行距),则当前行被攻击的列为 t[j](同列)、t[j] - diff 与 t[j] + diff(两条斜线落在第 i 行的位置),在范围内则标为不可放。再枚举 bin_record[k] 为真的列 k 递归。

class Solution(object):
    def transform(self, info):
        ans = []
        for i in range(len(info)):
            row = ["."] * len(info)
            row[info[i]] = "Q"
            row = "".join(row)
            ans.append(row)
        return ans

    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        t = []
        ans = []

        def check(i):
            if i == n:
                ans.append(t[:])
            else:
                bin_record = [True] * n
                for j in range(len(t)):
                    diff = i - j
                    bin_record[t[j]] = False
                    if t[j] - diff >= 0:
                        bin_record[t[j] - diff] = False
                    if t[j] + diff <= n - 1:
                        bin_record[t[j] + diff] = False
                for k in range(n):
                    if bin_record[k]:
                        t.append(k)
                        check(i + 1)
                        t.pop()

        check(0)
        for i in range(len(ans)):
            ans[i] = self.transform(ans[i])
        return ans

思路解析(解法二)

  • 与解法一等价:已放皇后 (j, t[j]) 对第 i 行的禁放列,正是同列与两条斜线延伸到第 i 行的交点列下标 t[j] ± (i - j)。
  • 实现:每行 O(n) 根据已有 t 刷新 bin_record,再 O(n) 尝试可放列;总思路仍是回溯,仅冲突检测的数据结构不同。
  • 复杂度:与解法一同阶;n ≤ 9 时足够。
posted @ 2026-10-12 00:19  Phier  阅读(3)  评论(0)    收藏  举报