线性结构-队列

1. 什么是队列

·FIFO 先进先出,一端进,另一端出。没有位置的概念,保证在任何时候可访问,删除的元素都是最近存入队列的元素

·数据集:一个有 0 个或多个元素的有穷线性表

·操作集

  1. 创建空队列 Queue()
  2. 判断队列为空 is_empty()
  3. 入队 enqueue()
  4. 出队 dequeue()
  5. 查看队列里最早进入的元素 peek()

2. 队列的实现

2.1 链表实现

队列要求首端插入,尾端删除,所以需要两个指针域分别指向头结点和尾结点,以保证O(1)的访问和删除。

对于单向链表,只能以尾结点为队尾,头结点为队首。(因为出队后,指针要沿着next的方向指向后一个元素)

class QueueUnderflow(ValueError):
    pass

class LNode():
    def __init__(self,elem,next_=None):
        self.elem = elem
        self.next = next_
        
    def __str__(self):
        return str(self.elem)       
    
class Queue():
    def __init__(self):
        self._head = None
        self._rear = None
        self._length = 0
        
    def is_empty(self):
        return self._head is None
    
    def enqueue(self,elem):
        n_Node = LNode(elem)
        if self._head is None:
            self._head = n_Node
            self._rear = n_Node
        else:
            n_Node = LNode(elem)
            self._rear.next = n_Node
            self._rear = n_Node
        
    def dequeue(self):
        if self._head is None:
            raise QueueUnderflow("in dequeue")
        else:
            p = self._head
            self._head = self._head.next
            return p.elem
        
    def peek(self):
        if self._head is None:
            raise QueueUnderflow("in peak")
        else:
            return self._head.elem

2.2 队列的顺序表实现

困难1:反复入队,出队操作,队尾最终会达到表存储区末端。所以采用循环顺序表

循环顺序表
  1. 队头变量q.head记录队列第一个元素的位置,队尾变量q.rear记录最后一个元素之后的第一个空位
  2. 队列元素保存在顺序表的一段连续单元里[q.head:q.rear]
  3. 入队 q.rear = (q.rear+1)%q.len 出队 q.head = (q.head+1)%q.len 队满的条件定义为(q.rear+1)%q.len == q.head
    在这里插入图片描述
    困难2:考虑定义一个可以自动扩充存储的队列类,不能直接用list的自动存储扩充机制,因为队列的元素可能是表里的任意一段
class QueueUnderflow(ValueError):
    pass

class SQueue():
    
    def __init__(self,init_len=8):
        self._len = init_len         # 存储区长度
        self._elems = [0]*init_len   # 元素存储
        self._head = 0               # 队头元素下标
        self._num = 0                # 元素个数,队尾的rear指针用self._head+self._num
        
    def is_empty(self):
        return self._num == 0
    
    def peek(self):
        if self.is_empty():
            raise QueueUnderflow
        return self._elems[self._head]
    
    def dequeue(self):
        if self.is_empty():
            raise QueueUnderflow
        e = self._elems[self._head]
        self._head = (self._head+1)%self._len
        self._num -= 1
        return e
    
    def enqueue(self,elem):
        if self._num == self._len:
            self._extend()
        self._elems[(self._head+self._num)%self._len] = elem
        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
        

3.队列的应用:迷宫问题求解

# 递归求解,通过函数递归的方式向前探查,通过函数返回的方式回溯

dirs = [(0,1),(1,0),(0,-1),(-1,0)]  # 四个相邻位置
def mark(maze,pos):        # 给迷宫走过的位置标记为2
    maze[pos[0]][pos[1]] = 2
    
def passable(maze,pos):  # 检查迷宫的位置是否可行
    return maze[pos[0]][pos[1]] == 0

def find_path(maze,pos,exit):
    mark(maze,pos)
    if pos == exit:
        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,exit):
                print(nextp,end=' ')
                return True
    return False

# 栈和回溯法,用栈来保存中间信息。   深度优先搜索
def maze_solver(maze,start,end):
    if start == end:
        print(start,end =' ')
        return True
    st = SStack()
    mark(maze,start)
    st.push((start,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_path(start,pos,end)  # 打印路径,即弹出栈中所有元素,以及当前位置
                return True
            if passable(maze,nextp):  # 遇到未探测的新位置
                st.push((pos,i+1))  # 该位置的下一方向入栈
                mark(maze,nextp)    # 栈里保存的元素都是标记过的
                st.push((nextp,0))  # 新位置入栈
                break  # 退出内存循环,下次迭代将以新栈顶为当前位置继续
    print("No path found.")
    
# 队列,逐步扩展,无回溯。  宽度优先搜索
def maze_solver(maze,start,end):
    if start == end:
        print(start,end =' ')
        return
    q = SQueue()
    mark(maze,start)
    q.enqueue(start)
    path = dict{}
    while not q.is_empty():
        pos = q.dequeue()
        for i in range(4):
            nextp = (pos[0]+dirs[i][0],pos[1]+dirs[i][1])
            if nextp == end:
                print_path(start,nextp,end)
                return True
            if passable(maze,nextp):
                path[nextp] = pos
                mark(maze,nextp)
                q.enqueue(nextp)
    print("No path.")

path ={}
pos = (3,2)
t = ((4,2),(4,3),(4,4),(5,4))
for i in t:
    nextp = i
    path[nextp] = pos
    pos = i

def print_path(path,pos):
    formerp = path[pos]
    while formerp in path:
        print(formerp,end=' ')
        formerp = path[formerp]
    print(formerp)
    
    
print_path(path,(5,4))  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值