[python算法:基础数据结构]利用栈和双端队列以及networkx可视化深度优先和广度优先搜索

'''队列迷宫'''
import numpy as np
import networkx as nx  
import matplotlib.pylab as plt
import random
class Deque:
    def __init__(self) -> None:
        '''认为最左即索引0为尾部,-1索引为头部'''
        self.items=[]
    def addFront(self,item):
        self.items.append(item)
    def addRear(self,item):
        self.items.insert(0,item)
    def removeFront(self):
        return self.items.pop()
    def removeRear(self):
        return self.items.pop(0)
    def isEmpty(self):
        if self.items==[]:
            return True
        else:
            return False
    def size(self):
        return len(self.items)

class Maze:
    def __init__(self) -> None:
        self.maze=[
        [1,1,1,1,1,1,1,1,1,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,0,0,1,1,0,0,1],
        [1,0,1,1,1,0,0,0,0,1],
        [1,0,0,0,1,0,0,0,0,1],
        [1,0,1,0,0,0,1,0,0,1],
        [1,0,1,1,1,0,1,1,0,1],
        [1,1,0,0,0,0,0,0,0,1],
        [1,1,1,1,1,1,1,1,1,1]
    ]
        self.route=[]
        self.path=[]
    def reset_default(self):
        '''恢复默认迷宫'''
        self.maze=[
        [1,1,1,1,1,1,1,1,1,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,0,0,1,1,0,0,1],
        [1,0,1,1,1,0,0,0,0,1],
        [1,0,0,0,1,0,0,0,0,1],
        [1,0,1,0,0,0,1,0,0,1],
        [1,0,1,1,1,0,1,1,0,1],
        [1,1,0,0,0,0,0,0,0,1],
        [1,1,1,1,1,1,1,1,1,1]
    ]
    
    def maze_generalizer(self,m=5,n=5):
        random.seed(114514)
        '''来源于https://zhuanlan.zhihu.com/p/363956880'''
        def inborder(x,y):
            return x<n and x>=0 and y<m and y>=0
        # 记录每个点是否经过过
        visited = [[False for i in range(m)] for j in range(n)] 
        # 记录迷宫形状,注意n行,m列的迷宫应该包含2n+1行,2m+1列
        # 因为a条平行的通路之间和两侧一共还有a+1堵墙
        map = [[0 for i in range(2*m+1)] for j in range(2*n+1)] 
        dx=[-1,1,0,0]
        dy=[0,0,-1,1]
        #分别是上下左右的位移矢量
        #起始点位置和终止点位置
        startposx=0
        startposy=0
        endposx=m-1
        endposy=n-1
        #px,py是指针当前指向的位置,visited[px][py]对应于map[2px+1][2py+1]
        px = startposx
        py = startposy
        visited[px][py]=True
        visitedpos=[(px,py)]#之前走过的所有点构成的列表
        map[2*px+1][2*py+1]=1
        sum=1 #经过点的总数,当达到mn时迷宫就构建好了
        while(sum<m*n):
            walkable=[] #存储px,py点前后左右四个点中能到达的点对应的i
            for i in range(4):
                #计算新的点的位置
                tx=px+dx[i]
                ty=py+dy[i]
                if inborder(tx,ty) and not visited[tx][ty]:#能到达就将i加入walkable
                    walkable.append(i)
            if not walkable or (px==endposx and py==endposy):
                #如果walkable为空,或已到达终点,则向之前的位置随机传送
                px,py=random.choice(visitedpos)
            else:
                #随机选择方向行进
                i = random.choice(walkable)
                px += dx[i]
                py += dy[i]
                visited[px][py]=True
                visitedpos.append((px,py))
                #将到达的点和前后两点之间的墙赋值为1(路)
                map[2*px+1][2*py+1]=1
                map[1+2*px-dx[i]][1+2*py-dy[i]]=1
                sum+=1
        self.maze=np.where(np.array(map)>0,0,1).tolist() #替换0,1
        print('成功生成新迷宫')
    
    def four_direction(self,x,y):
        '''返回上,下,左,右的坐标'''
        return (x+1,y),(x-1,y),(x,y-1),(x,y+1)
    def get_path(self):
        '''这个path里有很多路径,只有一个是到终点的'''
        current_node=self.path[-1] #终点节点
        while current_node[1]!=-1: #只要没到起点
            self.route.append(current_node[0])
            current_node=self.path[current_node[1]]
        self.route.append(current_node[0]) #将起点加入
        self.route.reverse() #反转列表,否则是反的

    def dfssearch(self,start_x,start_y,end_x,end_y):
        '''搜索路径,修改迷宫并修改可行路径'''
        stack=Stack()
        stack.push((start_x,start_y)) #添加起始位置
        
        while stack.size()>0:
            current_node=stack.peek() #当前节点
            if current_node==(end_x,end_y):#如果是终点              
                self.route=stack.items
                self.maze[start_x][start_y]='S' #标记起点
                self.maze[end_x][end_y]='E' #标记终点
                return True
            #搜索上下左右四个方向,看能不能走
            for dir in self.four_direction(*current_node): #元组解包
                if self.maze[dir[0]][dir[1]]==0:
                    stack.push(dir) #可以走,压入栈中
                    self.maze[dir[0]][dir[1]]=2 #标记为已经走过
                    break
            else: #四个方向都行不通(注意只有四个方向都行不通才会到这步,否则在内部就break了)
                self.maze[current_node[0]][current_node[1]]=2 #标记为已经走过
                stack.pop() #回退(即最后走的路出栈)
        '''如果到这了,说明栈空,没有路'''
        print('没有路')
        return False
    def bfssearch(self,start_x,start_y,end_x,end_y):
        '''搜索路径,修改迷宫并修改可行路径'''
        queue=Deque()
        queue.addFront(((start_x,start_y),-1)) #添加起始位置
        self.maze[start_x][start_y]='S' #标记起点
        
        '''这里队列存储的数据结构为((x坐标,y坐标),指示队列索引)'''
        while queue.size()>0:
            current_node=queue.removeRear() #初始点出列
            self.path.append(current_node) #加入到出列路径列表
            if current_node[0]==(end_x,end_y):
                '''找到了终点,这时path最后一个元素就是终点,处理path就能得到路径'''
                self.maze[end_x][end_y]='E' #标记终点
                self.get_path()             
                return True
            #搜索上下左右四个方向,看能不能走
            for dir in self.four_direction(*current_node[0]): #元组解包
                if self.maze[dir[0]][dir[1]]==0: #如果某方向能走
                    queue.addFront((dir,len(self.path)-1)) #入列时标注索引
                    #将这个方向的点加入队列(这里可能加入多个索引相同点,因为只出列一个)
                    self.maze[dir[0]][dir[1]]=2 #标记为已经走过
        else:
            print('没有搜索到路')
            return False
                   



    def draw(self,x=12,y=12):
        '''允许设置图像大小'''
        '''接受二维数组和路径集,返回图'''
        route=self.route #路径集,形如[(1,2),(2,3)]
        plt.figure(figsize=(x,y))
        plt.subplot(111)
        G=nx.Graph()
        rownum,colnum=len(self.maze),len(self.maze[0])
        map_data={(i,j):self.maze[i][j] for i in range(rownum) for j in range(colnum)} #地图信息,主要是利用二维数组的坐标和值
        pos={}
        for coordinate,attr in map_data.items():
            pos[coordinate]=coordinate
            G.add_node(coordinate,attribute=attr) #节点的坐标为id,属性代表是哪种(障碍还是通路)
            nx.draw_networkx_nodes(G,nodelist=[coordinate],pos={coordinate:coordinate},
                                    node_color='red' if attr==1 else 'yellow' if attr==2 else 'grey' if attr==0 else 'green' if attr=='E' else 'orange',label=coordinate)
        if self.route:
            link=[(route[nodenum],route[nodenum+1]) for nodenum in range(len(route)-1)]
            for index,node in enumerate(link):
                G.add_edge(node[0],node[1],order=index+1) #顺序
                nx.draw_networkx_edges(G,edgelist=[(node[0],node[1])],pos=pos,width=8)


maze=Maze()
maze.maze_generalizer(10,10) #生成m*2+1的迷宫
maze.dfssearch(1,1,19,19)
maze.draw(14,14)
maze.maze_generalizer(10,10) #生成m*2+1的迷宫
maze.bfssearch(1,1,19,19)
maze.draw(14,14)

结果:

深度优先搜索

 广度优先搜索

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值