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    
"""

从打印结果看,基于栈的搜索如果顺利,可能只探查不多的位置就找到出口,是一条路径;

基于队列的搜索是一种步步为营的搜索,只有在检查完所有与入口同样距离位置之后才更多前进一步

根据搜索过程的特点:把基于栈的搜索称为深度优先搜索,

基于队列的搜索称为宽度优先搜索

posted @ 2019-10-22 15:37  fly_bk  阅读(410)  评论(0编辑  收藏  举报