回溯法
- 1 括号生成
- 分析:
- 2 解数独
- 分析
- 代码
回溯法本质是的暴力枚举/遍历法,一般用递归实现。
当我们可以把问题分解为若干个步骤,每个步骤都有若干个选择的时候,若需要列出所有解答形式,则采用枚举法。
1 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
分析:
有效的括号一定是现有左括号,再有有括号,无论在哪个位置,左括号的数量要大于等于右括号。
- n=1时:
1个
(
,一个)
,第一个位置永远只能是(
,第二个位置就只剩下)
了,所以只有["()"]
- n=2时:
第一个只能是
(
,第二个可以是(
也可以是)
,
第二个是(
时,两个(
都用完了,第三个和第四个都只能是)
了
第二个是)时,目前‘(’个数等于‘)’个数,第三个只能是‘(’,第三个和第四个都只能是
)`了
pos | 0 | 1 | 2 | 3 |
---|---|---|---|---|
( | ( | ) | ) | |
( | ) | ( | ) |
综上,我们需要num_left,num_right计算目前已经有的左右括号的个数,只有右括号个数小于左括号个数的时候,下一个才能是右括号,否则(右括号个数等于左括号个数)下一个只能左括号,可以用递归的形式写。
#python
class Solution:def generateParenthesis(self, n: int) -> list[str]:def helper(index,n,right_num,left_num):if len(subs)==2*n:ans.append(''.join(subs))elif index<2*n:if index==0:subs.append('(')helper(index+1,n,right_num,left_num+1)else:if left_num<n:subs.append('(')helper(index+1,n,right_num,left_num+1)subs.pop()if right_num<left_num:subs.append(')')helper(index+1,n,right_num+1,left_num)subs.pop()subs = []ans = []helper(0,n,0,0)return ans
2 解数独
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
分析
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
可以遍历每一个数字,找不到的时候回溯,分析:
使用(i,j)表示当前遍历的位置,使用row[i]表示第i行已经存在的数字,col[j]表示第j列已经存在的数字,box[(i//3)*3+j//3]是当前位置所在格子已经存在的数,row[i],col[j],box[(i//3)*3+j//3]是哈希表方便查询。
边界条件:
- 当i==9的时候,代表所有格子都遍历完了
- 当j==9的时候,代表要换行了
- 当board[i][j]为数字时,不用填数字,直接下一个
非边界条件:
这个时候要从1-9里面选还没有在row[i],col[j],box[(i//3)*3+j//3]中出现的数字填进格子,如果helper(i,j+1) return true,说明可以填;否则,回溯,删掉数字继续遍历,如果所有数字都遍历完也没找到,return False
代码
class Solution:def solveSudoku(self, board: list[list[str]]) -> None:"""Do not return anything, modify board in-place instead."""def helper(board,row,col,box,i,j):if i==9:return Trueif j==9:return helper(board,row,col,box,i+1,0)if board[i][j]!='.':return helper(board,row,col,box,i,j+1)box_index = (i//3)*3+(j//3)for num in range(1,10):if num not in row[i] and num not in col[j] and num not in box[box_index]:row[i][num]=1col[j][num]=1box[box_index][num]=1board[i][j]=str(num)if helper(board,row,col,box,i,j+1):return Truedel row[i][num]del col[j][num]del box[box_index][num]board[i][j]='.'return Falsem = len(board)n = len(board[0])row = [{} for _ in range(m)]col = [{} for _ in range(n)]box = [{} for _ in range(m // 3 * n // 3)]for i in range(m):for j in range(n):num = board[i][j]if num!='.':num = int(num)row[i][num]=1col[j][num]=1box[(i//3)*3+(j//3)][num]=1helper(board,row,col,box,0,0)return board