第七章 回溯(六)
主要内容
1.回溯:都好难,行程、N皇后、数独
2.数组:二分
回溯题目
332.重新安排行程
思路分析
找到符合的路径后,直接返回
代码
class Solution:
def __init__(self):
self.res = ["JFK"]
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
# tickets: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
# 构造tickets_dict, 形式为{‘JFK’:["SFO","ATL"],'SFO':[ATL], ...}
tickets_dict = defaultdict(list)
for item in tickets:
tickets_dict[item[0]].append(item[1])
self.backtracking(tickets, tickets_dict, "JFK")
return self.res
def backtracking(self, tickets, tickets_dict, startpoint):
# 终止条件
if len(self.res) == len(tickets) + 1:
return True
# 对于待落地机场排序
tickets_dict[startpoint].sort()
# 选择
for _ in tickets_dict[startpoint]:
# 处理
endpoint = tickets_dict[startpoint].pop(0)
self.res.append(endpoint)
print('start:', startpoint, 'end:',endpoint,'res:',self.res)
# 回溯
if self.backtracking(tickets, tickets_dict, endpoint):
return True
# 回撤
self.res.pop()
tickets_dict[startpoint].append(endpoint)
51. N皇后
思路分析
代码
class Solution:
def __init__(self):
self.res = []
def solveNQueens(self, n: int) -> List[List[str]]:
chessboard = [['.'] * n for _ in range(n)]
self.backtracking(n, 0, chessboard)
return self.res
def backtracking(self, n, row, chessboard):
# 收集结果
if row == n:
temp = []
for i in range(n):
temp.append("".join(chessboard[i]))
self.res.append(temp)
return
# 选择
for col in range(n):
if self.isValid(row, col, chessboard):
# 放上处理
chessboard[row][col] = 'Q'
# 递归
self.backtracking(n, row + 1, chessboard)
# 回撤
chessboard[row][col] = '.'
def isValid(self, row, col, chessboard):
# 判断当前位置(row,col)放上皇后是否合法
# 判断列
for i in range(row):
if chessboard[i][col] == 'Q':
return False
# 判断斜45°角
i = row - 1
j = col - 1
while i >= 0 and j >= 0:
if chessboard[i][j] == 'Q':
return False
i -= 1
j -= 1
# 判断斜135°角
i = row - 1
j = col + 1
while i >= 0 and j < len(chessboard):
if chessboard[i][j] == 'Q':
return False
i -= 1
j += 1
return True
37. 解数独
思路分析
代码
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
self.backtracking(board)
def backtracking(self, board) -> bool:
for i in range(9): #遍历行
for j in range(9): #遍历列
if board[i][j] != ".":
continue
for k in range(1, 10):
if self.isValid(board, i, j, k):
board[i][j] = str(k)
if self.backtracking(board):
return True
board[i][j] = '.'
# 若数字1-9都不能成功填入空格,返回False无解
return False
return True
def isValid(self, board: List[List[str]], row: int, col: int, val: int) -> bool:
# 同列
for i in range(9):
if board[i][col] == str(val):
return False
# 同行
for j in range(9):
if board[row][j] == str(val):
return False
# 小方块
startrow = (row // 3) * 3
startcol = (col // 3) * 3
for i in range(startrow, startrow + 3):
for j in range(startcol, startcol + 3):
if board[i][j] == str(val):
return False
return True
数组题目(复习)
704. 二分查找
思路分析
左闭右闭,left==right有意义,left = mid + 1,right = mid - 1
代码
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
return mid
return -1
35.搜索插入位置
思路分析
遍历完后
l 及 l 右边的值大于t
r 及 r左边的值小于t
代码
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
return mid
# print(left, right, mid)
return left
34. 在排序数组中查找元素的第一个和最后一个位置
思路分析
# 存在有三种情况
# 1. target在nums区间外
# 2. target在区间内,但无值
# 3. target在区间内,有值
代码
class Solution:
# 存在有三种情况
# 1. target在nums区间外
# 2. target在区间内,但无值
# 3. target在区间内,有值
def searchRange(self, nums: List[int], target: int) -> List[int]:
left = self.searchLeft(nums, target)
right = self.searchRight(nums, target)
print(left,right)
# 情况一
if left == -2 or right == -2:
return [-1, -1]
# 情况三
if right - left > 1:
return [left + 1, right - 1]
# 情况二
return [-1, -1]
def searchLeft(self, nums, target):
left, right = 0, len(nums) - 1
leftboader = -2
while left <= right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid - 1
leftboader = right
return leftboader
def searchRight(self, nums, target):
left, right = 0, len(nums) - 1
rightboader = -2
while left <= right:
mid = left + (right - left) // 2
if nums[mid] > target:
right = mid - 1
else:
left = mid + 1
rightboader = left
return rightboader
27. 移除元素
思路分析
代码
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
left = 0
for right in range(len(nums)):
if nums[right] != val:
nums[left] = nums[right]
left += 1
return left
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
if not nums:
return -1
left, right = 0, len(nums) - 1
while left <= right:
#print('start:',nums)
#print('left:',left,'right:',right)
# 从左向右找为val的
while left <= right and nums[left] != val:
left += 1
#print('left:', left, 'val:', nums[left])
# 从右向做找非val的
while left <= right and nums[right] == val:
right -= 1
#print('right:', right, 'val:', nums[right])
# 找到后互换
if left < right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
#print('end:', nums)
#print(left, right)
return left