算法和数据机构学习笔记之队列
定义:
- 只允许在一端进行插入操作,而在另一端进行删除的操作的线性表
- 一种先进先出的线性表
- 允许插入的一端叫做队尾,允许删除的一端叫做队头
抽象数据类型:
init(初始化操作)
enquene(self,item) 若队列存在,插入新元素item到队列中,并成为队尾元素
dequene(self) 删除队列中队头元素,并返回队头元素
用数组实现队列:
思路:
数组实现队列时,要注意,随着不断进队,出队,head和tail都会持续往后移动。当tail移动到最右边,即使数组中还有空余空间,也无法继续往队列中添加元素了。这时可以用数据搬移。但是如果每一次出队,都要搬移数据,时间复杂度就是0(n)。要让时间复杂度尽可能变小,可以先在元素出队时,不搬移数据。若没有空间,再集中触发一次搬移数据的操作。
from typing import Optional
class ArrayQuene:
def __init__(self,capacity:int):
self.capacity=capacity
self.items=[]
self.head=0
self.tail=0
def enquene(self,item:str):
if self.tail==self.capacity:
return False
else:
#数据搬移
for i in range(0,self.tail-self.head):
self.items[i]=self.items[i+self.head]
self.tail=self.tail-self.head
self.head=0
#插入元素至队尾,队尾后移
self.items.insert(self.tail,item)
self.tail+=1
return True
def dequene(self):
# 若栈空
if self.head==self.tail:
return None
#若栈不为空
else:
#把队头元素从队头删掉
item=self.items[self.head]
self.items.pop(self.head)
self.head-=1
return item
#展示给开发者,用于测试
def __repr__(self):
return " ".join(item for item in self.items[self.head:self.tail])
if __name__ == "__main__":
q=ArrayQuene(10)
for i in range(0,9):
q.enquene(str(i))
print (q)
q.enquene("3")
print(q)
q.dequene()
print(q)
print(q.items)
队列的链式存储结构以及实现:
思路:
入队时,是从链队列尾部添加元素;出队时,从链队列的头部删除元素。入队时,在链表尾部插入结点。如果链表尾部元素为空,那么插入的元素就是第一个元素。出队时,头结点的后继指针出队,将头结点的后继指针改为它后面的结点。
from typing import Optional
class Node:
def __init__(self, data: str, next=None):
self.data = data
self.next = next
class LinkedQueue:
def __init__(self):
self.head: Optional[Node] = None
self.tail: Optional[Node] = None
def enqueue(self, value: str):
new_node = Node(value)
#如果存在队尾元素
if self.tail:
self.tail.next = new_node
else:
self.head = new_node
self.tail = new_node
def dequeue(self) -> Optional[str]:
#如果存在对头元素
if self.head:
value = self.head.data
self.head = self.head.next
if not self.head:
self.tail = None
return value
def __repr__(self) -> str:
values = []
current = self.head
while current:
values.append(current.data)
current = current.next
return "->".join(value for value in values)
if __name__ == "__main__":
q = LinkedQueue()
for i in range(10):
q.enqueue(str(i))
print(q)
for _ in range(3):
q.dequeue()
print(q)
循环队列
因为顺序队列需要做数据搬移,时间性能上有较大的的改进空间。循环队列可以弥补顺序队列的不足。当有元素出队有元素进队,使得前面的头指针所指的元素的下标不为0,头结点前还有空位时,后面的元素直接插入头结点前的空位,使得队列头尾相接。
问题在于:当head == tail时,队列为空。那么现在这种情况,如果队列满,我们发现head也可以是head == tail.
所以可以采取空余一个位置的办法。
这种情况下,队列满的条件是(tail+1)%QueneSize=head
长度为(tail+QueneSize-head)%QueneSize
from typing import Optional
from itertools import chain
class CircularQueue:
def __init__(self, capacity):
self._items = []
self._capacity = capacity + 1
self._head = 0
self._tail = 0
def enqueue(self, item: str) -> bool:
if (self._tail + 1) % self._capacity == self._head:
return False
self._items.append(item)
self._tail = (self._tail + 1) % self._capacity
return True
def dequeue(self) -> Optional[str]:
if self._head != self._tail:
item = self._items[self._head]
self._head = (self._head + 1) % self._capacity
return item
def __repr__(self) -> str:
if self._tail >= self._head:
return " ".join(item for item in self._items[self._head : self._tail])
else:
return " ".join(item for item in chain(self._items[self._head:], self._items[:self._tail]))
if __name__ == "__main__":
q = CircularQueue(5)
for i in range(5):
q.enqueue(str(i))
q.dequeue()
q.dequeue()
q.enqueue(str(5))
print(q)