递归应用场景
- 各种数学问题,如八皇后问题、汉诺塔、阶乘问题、迷宫问题、球和篮子问题等
- 各种算法中也会使用到递归,比如快排、归并排序、二分查找、分治算法等
- 能够用栈解决的问题
- 递归的优点就是代码比较简洁
迷宫问题(Python版)
迷宫示意图, 红色部分表示墙,绿色部分表示通路,第一张图为初始迷宫,第二张为回溯之后的迷宫
代码实现
class MiGong:def __init__(self):# 创建一个 8*7 嵌套列表,模拟迷宫self.map = []row = 8col = 7# 初始化迷宫# 1表示墙,0表示可通行# 将第0行设置为墙for i in range(row):ls = []for j in range(col):if i == 0 or i == 7 or j == 0 or j == 6:ls.append(1)else:ls.append(0)self.map.append(ls)self.map[3][1] = 1self.map[3][2] = 1self.map[4][3] = 1self.print_map()# 打印迷宫def print_map(self):row = len(self.map)col = len(self.map[0])for i in range(row):for j in range(col):print(self.map[i][j], end=' ')print()print()def find_way(self, i: int, j: int) -> True:"""map: 表示迷宫地图(i, j): 老鼠的起始位置map[i][j] = 0: 表示该位置没有走过map[i][j] = 1: 表示该位置是墙(走不了)map[i][j] = 2: 表示该位置是通路(可以走)map[i][j] = 3: 表示这个位置是死路上的一个节点(走不了)寻路策略:下 -> 右 -> 上 -> 左map[6][5] = 2 表示走到了终点(终点位置为(6,5))"""if self.map[6][5] == 2:return Trueelse:if self.map[i][j] == 0:# 假设可以走通,先设为2self.map[i][j] = 2if self.find_way(i + 1, j): # 向下走return Trueelif self.find_way(i, j + 1): # 向右走return Trueelif self.find_way(i - 1, j): # 向上走return Trueelif self.find_way(i, j - 1): # 向左走return Trueelse: # 死路,走不通,设为3self.map[i][j] = 3return Falseelse: # self.map[i][j] == 1,2,3 ,都表示走不通或已走过return Falsemg = MiGong()
mg.find_way(1, 1)
mg.print_map()
八皇后问题
思路分析
1、先将第一个皇后放在第一行第一列的位置 2、第二个皇后先放在第二行第一列的位置,然后判断是否冲突,如果冲突,则放在第二行第二列,继续判断,如果冲突,将第二个皇后放在第二行第三列... 依次把第二行所有列都尝试一遍,直到找到合适的一列,摆放第二个皇后 3、接着摆放第三个皇后,依次尝试放在第三行的第一列、第二列、第三列...直到找到一个合适的列摆放第三个皇后 4、同样的摆放第四个、第五个、第六个、第七个、第八个皇后,当第八个皇后也放到第八行的正确位置时,此时找到了一个正确解 5、当第八个皇后摆放好后,即第八个皇后第一次找到正确的位置后,开始回溯,查找其他摆放方法,即将第一个皇后放在第一行第一列这个位置上的所有正确解找出来 6、然后将第一个皇后放在第一行第二列的位置,重复 2-5 的步骤 7、依次将第一个皇后放在第一行第三、四、五、六、七、八列的位置,重复 2-5 的步骤,得到八皇后问题的全部解法(92)说明:理论上棋盘应该是一个二维数组,但是实际上可以通过算法,用一个一维数组解决,一维数组的下标对应行标,数组的元素对应列标,如 arr[8] = [0, 4, 7, 5, 2, 6, 1, 3] 表示:第一个皇后放在第 0 行 第 0 列的位置第二个皇后放在第 1 行 第 4 列的位置第三个皇后放在第 2 行 第 7 列的位置第四个皇后放在第 3 行 第 5 列的位置第五个皇后放在第 4 行 第 2 列的位置第六个皇后放在第 5 行 第 6 列的位置第七个皇后放在第 6 行 第 1 列的位置第八个皇后放在第 7 行 第 3 列的位置 即 arr[i] = val 表示第 i 个皇后摆放的位置是第 i 行 第 val 列
代码实现
class Queen:count = 0 # 全部的正确解def __init__(self, num: int):self.num = num # 皇后的数量# 用列表表示一维数组,记录皇后摆放的正确位置self.arr = []# 初始化数组for i in range(num):self.arr.append(-1)def check_position(self, n: int) -> bool:"""摆放第 n 个皇后在某个位置时,判断是否与前面的 n-1 皇后产生冲突,不冲突返回Truen 从 0 开始"""for i in range(n):# arr[i] = val 表示第 i 个皇后摆放的位置是第 i 行 第 val 列if self.arr[i] == self.arr[n]: # 有两个皇后在同一列return False# if n - i == self.arr[n] - self.arr[i]: # 有两个皇后在正对角线上# # 正对角线判断:x2 - x1 == y2 - y1# # (x1, y1) = (i, self.arr[i])# # (x2, y2) = (n, self.arr[n])# return False# if n - i == self.arr[i] - self.arr[n]: # 有两个皇后在反对角线上# # 反对角线上判断:x2 - x1 == y1 - y2# # (x1, y1) = (i, self.arr[i])# # (x2, y2) = (n, self.arr[n])# return False# 合并在一起写if abs(n - i) == abs(self.arr[n] - self.arr[i]): # 有两个皇后在对角线上return Falsereturn Truedef place_queen(self, n: int):"""放置第 n 个皇后,n 从0开始"""if n == self.num: # 全部皇后已经放完,得到一个正确解self.count += 1print(f'第{self.count}种的解法为{self.arr}')return# 从第 n 行的第一列开始,依次尝试放置这第 n 个皇后,# 看哪个位置可以放,即不会与前面的 n-1 个皇后产生冲突for i in range(self.num): # i 表示列数self.arr[n] = i # 把第 n 个皇后放置在第 n 行第 i 列# 检查把第 n 个皇后放置在第 n 行第 i 列后是否与前面的 n-1 个皇后产生冲突if self.check_position(n): # 不产生冲突# 如果不产生冲突,则确定了该皇后的放置位置,则开始放置下一个皇后self.place_queen(n + 1)# 如果冲突,标明第 n 行的第 i 列不能放,继续尝试第 i+1 列def print_arr(self):"""打印数组"""for i in self.arr:print(i, end=' ')print()q = Queen(8)
q.place_queen(0) # 从第一个皇后开始,从 0 开始
print('八皇后的全部解法有%s种' % q.count)