文章目录
- 前言
- 一、深度优先搜索
- 1、栈
- 2.深度优先搜索实现迷宫
- 二、广度优先搜索
- 1.队列
- 2.广度优先搜索实现迷宫
- 三、 进一寸有进一寸的欢喜
前言
搜索是算法中很基础的东西,目前笔者将其分类为深度优先搜索、广度优先搜索、树搜索。
我们现在探究迷宫问题可以用到深度优先搜索和广度优先搜索解决,两种方法各有优缺
一、深度优先搜索
1、栈
在使用深度优先时我们先了解一下栈
笔者将栈喻为盒子,放东西的时候要从下往上放拿的时候要从上
栈的实现:
(铁汁不懂类和对象可以跳过这段程序哦)
class Stack: # 类
def __init__(self): # (个人理解)初始化类里面的变量
self.stack = [] # 定义一个列表
def push(self,element): # 进栈
self.stack.append(element)
def pop(self): # 出栈
return self.stack.pop()
def get_top(self): # 栈顶元素
if len(self.stack) > 0:
return self.stack[-1]
else: # 栈空报错
raise IndexError("Stack is empty")
def is_empty:
return len(self.stack) == 0
2.深度优先搜索实现迷宫
- 思路:从一个节点出发,任意方向找下一个能走的点,一直走到死胡同,退回上一个节点再找出路
- 回溯法:一条道上走不通回到上一个路口换条路走
- 使用栈存储路径
- 缺点:不是最短路径
- 优点:内存占用小
迷宫问题
给定一个迷宫maze任意出入口,0是路1是墙,请找到一条路打印出路的下标
代码:
maze = [
[0,1,0,0,0,0,0,0,0,0],
[0,0,1,0,0,0,1,0,1,0],
[1,0,0,0,1,1,0,0,1,0],
[1,1,1,0,0,0,0,0,0,0],
[1,0,0,0,0,1,1,0,0,0],
[1,1,1,0,0,1,1,1,0,0],
[1,1,0,0,1,0,0,1,1,0],
[1,1,0,0,0,0,0,0,1,1],
[0,0,0,1,0,0,1,0,0,0],
[1,1,1,1,0,1,1,1,1,0]
]
# lambda后的if判断防止越界
dirs = [
lambda x,y:(x+1,y) if x<len(maze)-1 else (x,y),
lambda x,y:(x-1,y) if x>0 else (x,y),
lambda x,y:(x,y+1) if y<len(maze)-1 else (x,y),
lambda x,y:(x,y-1) if y>0 else (x,y)
]
def maze_path(x1,y1,x2,y2):
stack = [] # 定义一个栈(列表)存储路径
stack.append((x1,y1)) # 入口导入
# 因为当没有路时会退回,当所有路径都退出来时证明没路了(stack中没有值了)
while len(stack) > 0:
curNode = stack[-1] # 定义变量读取当前位置(初始化为入口)
if curNode[0] == x2 and curNode[1] == y2: # 终点
for i in stack:
print(i) # 打印路径
return True
for i in dirs: # 在当前位置找下一个节点
nextNode = i(curNode[0],curNode[1]) # 下一个要走的节点
if maze[nextNode[0]][nextNode[1]] == 0: # 判断下一个节点能不能走
stack.append(nextNode) # 可以走加入路径中
maze[nextNode[0]][nextNode[1]] = 2 # 在地图上标记走过
break
else: # 四个方向没有可以走的路
maze[nextNode[0]][nextNode[1]] = 2
stack.pop() # 回溯
else:
print("没路啦")
return False
maze_path(0,0,9,9)
二、广度优先搜索
1.队列
队列分为环形队列,双向队列,单向队列
单向队列就是从一头进去从另一头出来
双向队列就是两头都能进出
在这里我们主要用到双向队列环形队列就不赘述了请移步到视频学习
- 双向队列内置函数(记不住可以用列表代替哦)
from collection import deque
q = deque([1,2,3],4) # 队列的大小是4个元素
q.append(4) # 右边进队
q.pop() # 右边出队
q.appendleft() # 左边进队
q.popleft() # 左边出队
2.广度优先搜索实现迷宫
- 思路:从一个节点开始,寻找所有接下来能走的节点,不断寻找直到找到出口
- 使用队列(列表)存储当前考虑的节点
- 框框是队列
- 优点:当找到最短路径时停止
- 缺点:需要更多的内存
from collections import deque
maze = [
[0,1,0,0,0,0,0,0,0,0],
[0,0,1,0,0,0,1,0,1,0],
[1,0,0,0,1,1,0,0,1,0],
[1,1,1,0,0,0,0,0,0,0],
[1,0,0,0,0,1,1,0,0,0],
[1,1,1,0,0,1,1,1,0,0],
[1,1,0,0,1,0,0,1,1,0],
[1,1,0,0,0,0,0,0,1,1],
[0,0,0,1,0,0,1,0,0,0],
[1,1,1,1,0,1,1,1,1,0]
]
dirs = [
lambda x,y:(x+1,y) if x<len(maze)-1 else (x,y),
lambda x,y:(x-1,y) if x>0 else (x,y),
lambda x,y:(x,y-1) if y>0 else (x,y),
lambda x,y:(x,y+1) if y<len(maze[x])-1 else (x,y)
]
def print_r(path):
curNode = path[-1]
realpath = [] # 存储最短路径
while curNode[2] != -1:
realpath.append(curNode[0:2])
curNode = path[curNode[2]] # 通过curNode[2](第三个元素)找到这条路的上一个节点存在路径中
realpath.append(curNode[0:2]) # 起点
realpath.reverse()
for i in realpath:
print(i)
def maze_path(x1,y1,x2,y2):
queue = deque()
queue.append((x1,y1,-1)) # 起点
# path[(0,0,-1)]存储所有路径的节点,并存储该节点的上一个节点的下标
path = []
while len(queue) > 0:
# 将队列中队首的元素赋值到当前位置,并将坐标移除队列,再加如到所有路径中去
curNode = queue.popleft()
path.append(curNode)
print(queue)
print(path)
if curNode[0] == x2 and curNode[1] == y2:
print_r(path)
return True
# 因为广度优先搜索不需要回溯,所以当路走不通时我们不用管他
for i in dirs:
nextNode = i(curNode[0],curNode[1])
if maze[nextNode[0]][nextNode[1]] == 0:
queue.append((nextNode[0],nextNode[1],len(path)-1))
maze[nextNode[0]][nextNode[1]] = 2
else:
print("没路啦")
return False
maze_path(0,0,9,9)