递归:思路简单但效率低(建立函数的副本,消耗大量时间和内存)。能用迭代就不用递归。
递推公式+递推终止条件
Fibonacci数列
# 递归 class Solution: def fib(self, N: int) -> int: if N <= 1: return N return self.fib(N-1) + self.fib(N-2) # 迭代 class Solution: def fib(self, N: int) -> int: if N <= 1: return N tmp1 = 0 tmp2 = 1 for i in range(2, N+1): res = tmp1 + tmp2 tmp1 = tmp2 tmp2 = res return res
函数调用自身,注意递归的停止条件。分为调用和回溯两个阶段。
任意长度的字符串反向,递归实现
# 需要额外存储空间 def reverseStr(string): if string == None or len(string) == 0: return None if len(string) == 1: return string return reverseStr(string[1:])+string[0]
#leetcode,O(1)额外空间,原地修改。双指针 class Solution: def reverseString(self, s: List[str]) -> None: """ Do not return anything, modify s in-place instead. """ if s == None or len(s) <= 1: return None i, j = 0, len(s)-1 while i<j: s[i], s[j] = s[j], s[i] i += 1 j -= 1 return
# 超时的递归解法 class Solution: def reverseString(self, s: List[str]) -> None: """ Do not return anything, modify s in-place instead. """ if s == None or len(s) <= 1: return None cur = s.pop(0) self.reverseString(s) s.append(cur)
汉诺塔问题:
def move(n, a, b, c): """n个盘子从a借助b移动到c上""" if n==1: print(a+'->'+c) else: move(n-1, a, c, b) move(1, a, b, c) move(n-1, b, a, c)
八皇后问题:
当在棋盘上放置了几个皇后且不会相互攻击。但是选择的方案不是最优的,因为无法放置下一个皇后。此时该怎么做?回溯:回退一步,来改变最后放置皇后的位置并且接着往下放置。如果还是不行,再回溯。
一行只可能有一个皇后且一列也只可能有一个皇后。这意味着没有必要再棋盘上考虑所有的方格。按行往下找皇后,对于每个皇后的位置只需要按列循环即可。对于所有的主对角线有:行号 - 列号 = 常数,对于所有的次对角线有 行号 + 列号 = 常数。
class Solution: def solveNQueens(self, n: int) -> List[List[str]]: def could_place(row, col): # row这一行是没有放置过的行,要检查col这一列、(row,col)所占两条对角线有没有被放置过,如果都没有,(row,col)可以放皇后 return not (cols[col]+hill_diagonals[row-col]+\ dale_diagonals[row+col]) def place_queen(row, col): queens.add((row, col)) # 放皇后,记录位置,标记列和两对角线 cols[col] = 1 hill_diagonals[row-col] = 1 dale_diagonals[row+col] = 1 def remove_queen(row, col): queens.remove((row, col)) # 移除皇后,清空列和两对角线的标记 cols[col] = 0 hill_diagonals[row-col] = 0 dale_diagonals[row+col] = 0 def add_solution(): # 如果找到一个解,按要求记录下来 solution = [] for _, col in sorted(queens): solution.append('.'*col + 'Q' + '.'*(n-col-1)) output.append(solution) def backtrack(row): # 从第一行row=0开始放置皇后,放到n-1行 for col in range(n): # 对于确定的row,遍历所有列col if could_place(row, col): place_queen(row, col) # 如果(row, col)可以放皇后,就放 if row == n-1: # 如果已经放了最后一个,说明找到一个解 add_solution() else: # 没有放到最后一个的话 backtrack(row+1) # 去找row行之后所有可能的放置解法 remove_queen(row, col) # 不管是哪种情况都要回溯,移除当前皇后,进入(row, col+1)的情况 cols = [0] * n hill_diagonals = [0] * (2 * n -1) dale_diagonals = [0] * (2 * n -1) queens = set() output = [] backtrack(0) return output
分治:将问题分成几个小模块,逐一解决。
二分搜索
# 最普通的情况,规定有序数组不重复 class Solution: def search(self, nums: List[int], target: int) -> int: if nums == None or len(nums) == 0: return -1 low = 0 high = len(nums) - 1 while low <= high: # 双端闭区间[low, high]查找 mid = (low + high) // 2 if nums[mid] == target: return mid elif nums[mid] > target: high = mid - 1 elif nums[mid] < target: low = mid + 1 return -1
# 寻找左侧边界的二分搜索。初始化 right = nums.length,决定了「搜索区间」是 [left, right),所以决定了 while (left < right),同时也决定了 left = mid + 1 和 right = mid # 因为需找到 target 的最左侧索引,所以当 nums[mid] == target 时不要立即返回,而要收紧右侧边界以锁定左侧边界。 def search(nums, target): if nums == None or len(nums) == 0: return -1 low = 0 high = len(nums) while low < high: # [low, high) 上搜索 mid = (low + high) // 2 if nums[mid] == target: high = mid # 找到target之后不要立即返回,缩小搜索区间上界,在[low, mid)中继续搜索,锁定左侧边界low elif nums[mid] > target: high = mid elif nums[mid] < target: low = mid + 1 if low == len(nums): # target 比所有数都大 return -1 return low if nums[low] == target else -1 # 如果找到,low应该指向左侧边界
# 寻找右侧边界的二分搜索 def search(nums, target): if nums == None or len(nums) == 0: return -1 low = 0 high = len(nums) while low < high: # [low, high) 上搜索 mid = (low + high) // 2 if nums[mid] == target: low = mid + 1 # 找到target之后不要立即返回,缩小搜索区间下界,在[mid+1, high)中继续搜索,锁定右侧边界high-1 elif nums[mid] > target: high = mid elif nums[mid] < target: low = mid + 1 if low == len(nums): # target 比所有数都大 return -1 return low-1 if nums[low-1] == target else -1 # 若找到,最后low == high,右侧边界在 high-1
# 递归实现二分搜索 class Solution: def search(self, nums: List[int], target: int) -> int: if nums == None or len(nums) == 0: return -1 return self.recursiveSearch(nums, 0, len(nums)-1, target) def recursiveSearch(self, nums, low, high, target): if low > high: # 双端闭区间搜索 return -1 mid = (low+high)//2 if nums[mid] == target: return mid elif nums[mid] > target: return self.recursiveSearch(nums, low, mid-1, target) elif nums[mid] < target: return self.recursiveSearch(nums, mid+1, high, target) return -1