06.队列、python标准库中的双端队列、迷宫问题
class QueueUnderflow(ValueError): """队列为空""" pass class SQueue: def __init__(self, init_len=5): self._len = init_len # 存储区长度 self._elems = [0] * init_len # 元素存储 self._head = 0 # 表头元素下标 self._num = 0 # 元素个数 def is_empty(self): return self._num == 0 def peek(self): """查看队头元素""" if self._num == 0: raise QueueUnderflow return self._elems[self._head] def dequeue(self): """出队""" if self._num == 0: raise QueueUnderflow e = self._elems[self._head] self._head = (self._head + 1) % self._len self._num -= 1 return e def enqueue(self, e): """入队""" if self._head == self._len - 1: self._extend() self._elems[(self._head + self._num) % self._len] = e self._num += 1 def _extend(self): """扩容操作""" old_len = self._len self._len *= 2 new_elems = [0] * self._len for i in range(old_len): new_elems[i] = self._elems[(self._head + i) % old_len] self._elems, self._head = new_elems, 0 def print(self): """打印从队头开始""" end = self._head + self._num print("list:", self._elems[self._head:end])
python的deque类:
import collections # deque双端队列,支持元素的两端插入和删除,采用一种双链表技术实现 d = collections.deque() d.append(1) d.append(2) d.appendleft(0) print(d) # deque([0, 1, 2])
迷宫的递归求解:
def make(maze, pos): """标记maze为已走过""" maze[pos[0]][pos[1]] = 2 def passable(maze, pos): """检查maze位置是否可行""" return maze[pos[0]][pos[1]] == 0 def find_path(maze, pos, end): dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 东,南,西,北 make(maze, pos) if pos == end: print(pos, end=' ') return True for i in range(4): nextp = [pos[0] + dirs[i][0], pos[1] + dirs[i][1]] if passable(maze, nextp): if find_path(maze, nextp, end): print(pos, end=' ') return True return False def mazes(): """初始化迷宫""" map = [] for i in range(8): map.append([]) for j in range(7): if i == 0 or i == 7 or j == 0 or j == 6: map[i].append(1) else: map[i].append(0) return map def showMap(map): """打印迷宫""" row, col = len(map), len(map[0]) for i in range(row): for j in range(col): print(map[i][j], end='\t') print() print() if __name__ == '__main__': mmap1 = mazes() showMap(mmap1) print(find_path(mmap1, [1, 1], [6, 5])) showMap(mmap1) """ 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 [6, 5] [5, 5] [4, 5] [3, 5] [2, 5] [1, 5] [1, 4] [1, 3] [1, 2] [1, 1] True 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 1 1 1 1 1 1 """
基于栈的回溯解决迷宫问题:
def maze_solver(maze, start, end): """基于栈的回溯解决迷宫问题 算法框架: 入口start相关信息(位置和尚未探索方向)入栈; while 栈不空: 弹出栈顶元素作为当前位置继续搜索 while 当前位置存在未走过的方向: 求出下一探测位置nextp if nextp 是出口: 输出路径并结束 if nextp 未走过: 将当前位置和nextp顺序入栈并退出内层循环 """ dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 东,南,西,北 if start == end: print(start) return st = SStack() make(maze, start) # 标记走过 # 4个方向分别编码为0,1,2,3,表示dirs的下标 st.push((start, 0)) # 入口和方向0入栈 while not st.is_empty(): pos, nxt = st.pop() for i in range(nxt, 4): nextp = [pos[0] + dirs[i][0], pos[1] + dirs[i][1]] # 算出下一位置 if nextp == end: # 到达出口 print(nextp, end=' ') # 终点位置 print(pos, end=' ') # 当前位置 st.print() # 经过的位置 return if passable(maze, nextp): # 位置可行 st.push((pos, i + 1)) # 当前位置,与下一方向入栈,因为现在走的是i,如果回退就应走i+1了 make(maze, nextp) st.push((nextp, 0)) # 新位置入栈,方向都是从0开始 break # 退出内层循环,下层讲以新栈顶为当前位置继续 print("no path found.") if __name__ == '__main__': mmap1 = mazes() showMap(mmap1) print(maze_solver(mmap1, [1, 1], [6, 5])) """ 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 [6, 5] [5, 5] [([4, 5], 2), ([3, 5], 2), ([2, 5], 2), ([1, 5], 2), ([1, 4], 1), ([1, 3], 1), ([1, 2], 1), ([1, 1], 1)] None 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 """
基于队列的迷宫求解算法:
def maze_solver_queue(maze, start, end): """基于队列的迷宫求解算法: 基本框架: 将start标记为已到达 start入队 while 队列里还有未充分探查的位置: 取出一个位置pos 检查pos的相邻位置 遇到end结束 未探查的都mark并入队 队列空,探索失败 """ dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 东,南,西,北 if start == end: print(start) return qu = SQueue() make(maze, start) qu.enqueue(start) while not qu.is_empty(): pos = qu.dequeue() for i in range(4): nextp = [pos[0] + dirs[i][0], pos[1] + dirs[i][1]] # 算出下一位置 if passable(maze, nextp): if nextp == end: print("path find") return make(maze, nextp) qu.enqueue(nextp) print("No path.") if __name__ == '__main__': mmap1 = mazes() showMap(mmap1) print(maze_solver_queue(mmap1, [1, 1], [6, 5])) showMap(mmap1) """ 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 path find None 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1 2 2 2 2 2 1 1 2 2 2 2 2 1 1 2 2 2 2 2 1 1 2 2 2 2 2 1 1 2 2 2 2 0 1 1 1 1 1 1 1 1 """
从打印结果看,基于栈的搜索如果顺利,可能只探查不多的位置就找到出口,是一条路径;
基于队列的搜索是一种步步为营的搜索,只有在检查完所有与入口同样距离位置之后才更多前进一步
根据搜索过程的特点:把基于栈的搜索称为深度优先搜索,
基于队列的搜索称为宽度优先搜索