递归应用场景
- 各种数学问题,如八皇后问题、汉诺塔、阶乘问题、迷宫问题、球和篮子问题等
- 各种算法中也会使用到递归,比如快排、归并排序、二分查找、分治算法等
- 能够用栈解决的问题
- 递归的优点就是代码比较简洁
迷宫问题(Python版)
迷宫示意图, 红色部分表示墙,绿色部分表示通路,第一张图为初始迷宫,第二张为回溯之后的迷宫
代码实现
class MiGong:
def __init__(self):
# 创建一个 8*7 嵌套列表,模拟迷宫
self.map = []
row = 8
col = 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] = 1
self.map[3][2] = 1
self.map[4][3] = 1
self.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 True
else:
if self.map[i][j] == 0:
# 假设可以走通,先设为2
self.map[i][j] = 2
if self.find_way(i + 1, j): # 向下走
return True
elif self.find_way(i, j + 1): # 向右走
return True
elif self.find_way(i - 1, j): # 向上走
return True
elif self.find_way(i, j - 1): # 向左走
return True
else: # 死路,走不通,设为3
self.map[i][j] = 3
return False
else: # self.map[i][j] == 1,2,3 ,都表示走不通或已走过
return False
mg = 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 皇后产生冲突,不冲突返回True
n 从 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 False
return True
def place_queen(self, n: int):
"""放置第 n 个皇后,n 从0开始"""
if n == self.num: # 全部皇后已经放完,得到一个正确解
self.count += 1
print(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)