队列
概念
概念:
队列是项的有序集合,其中添加新项的一端称为队尾,移除项的一端称为队首。当一个元素从队尾进入队列时,一直向队首移动,直到它成为下一个需要移除的元素为止。最近添加的元素必须在队尾等待。集合中存活时间最长的元素在队首,这种排序成为 FIFO,先进先出,也被成为先到先得。
案例:
队列的最简单的例子是我们平时不时会参与的列。抢购火车票, 排队等待电影,在杂货店的收营台等待,在自助餐厅排队等待(这样我们可以弹出托盘栈)。行为良好的线或队列是有限制的,因为它只有一条路,只有一条出路。不能插队,也不能离开。你只有等待了一定的时间才能到前面。
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
应用
队列的应用:
我们的计算机实验室有 30 台计算机与一台打印机联网。当学生想要打印时,他们的打印任务与正在等待的所有其他打印任务“一致”。第一个进入的任务是先完成。如果你是最后一个,你必须等待你前面的所有其他任务打印。
除了打印队列,操作系统使用多个不同的队列来控制计算机内的进程。下一步做什么的调度通常基于尽可能快地执行程序和尽可能多的服务用户的排队算法。此外,当我们敲击键盘时,有时屏幕上出现的字符会延迟。这是由于计算机在那一刻做其他工作。按键的内容被放置在类似队列的缓冲器中,使得它们最终可以以正确的顺序显示在屏幕上。
Python实现队列
队列的抽象数据类型定义:队列的抽象数据类型应该由以下结构和操作定义。队列操作如下:
- Queue() # 创建一个空的新队列。 它不需要参数,并返回一个空队列。
- enqueue(item) # 将新项添加到队尾。 它需要 item 作为参数,并不返回任何内容。
- dequeue() # 从队首移除项。它不需要参数并返回 item。 队列被修改。
- isEmpty() # 查看队列是否为空。它不需要参数,并返回布尔值。
- size() # 返回队列中的项数。它不需要参数,并返回一个整数。
class Queue():
def __init__(self):
self.items = []
def enqueue(self, item):
self.items.insert(0, item)
def dequeue(self):
if not self.isEmpty():
return self.items.pop()
def isEmpty(self):
return self.items == []
def size(self):
return len(self.items)
q = Queue()
q.enqueue(1) # 向队列加入数据
q.enqueue(2)
q.enqueue(3)
print(q.size()) # 3
print(q.dequeue()) # 1
print(q.dequeue()) # 2
print(q.dequeue()) # 3
案例: 烫手的山芋
游戏介绍:
6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时1秒后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时7秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。
分析:
为了模拟这个圈,我们可以使用队列。假设游戏开始时,排在队列中的第一个(队首)的孩子手里拿着山芋。游戏开始后,拿着山芋的孩子出队列然后再入队列,将山芋传递给下一个孩子。每当山芋到队首孩子手里后,队首的孩子先出队列再入队列,一次类推。当传递六次后,手里有山芋的孩子淘汰,游戏继续,继续传递山芋。
names = ['A','B','C','D','E','F']
q = Queue()
for name in names:
q.enqueue(name)
while q.size() > 1:
for i in range(7):
kid = q.dequeue()
q.enqueue(kid)
q.dequeue()
print(q.dequeue())
基本数据结构-双端队列 (Deque)
双端队列
- 概念:deque(也称为双端队列)是与队列类似的项的有序集合。它有两个端部,首部和尾部,并且项在集合中保持不变。
- 特性:deque 特殊之处在于添加和删除项是非限制性的。可以在前面或后面添加新项。同样,可以从任一端移除现有项。在某种意义上,这种混合线性结构提供了单个数据结构中的栈和队列的所有能力。
- 注意:即使 deque 可以拥有栈和队列的许多特性,它不需要由那些数据结构强制的 LIFO 和 FIFO 排序。这取决于你如何持续添加和删除操作。
Python实现Deque
Deque的抽象数据类型定义:Deque的抽象数据类型应该由以下结构和操作定义。其中元素可以从首部或尾部的任一端添加和移除。Deque操作如下:
- Deque() # 创建一个空的新 deque。它不需要参数,并返回空的 deque。
- addFront(item) # 将一个新项添加到 deque 的首部。它需要 item 参数 并不返回任何内容。
- addRear(item) # 将一个新项添加到 deque 的尾部。它需要 item 参数并不返回任何内容。
- removeFront() # 从 deque 中删除首项。它不需要参数并返回 item。deque 被修改。
- removeRear() # 从 deque 中删除尾项。它不需要参数并返回 item。deque 被修改。
- isEmpty() # 测试 deque 是否为空。它不需要参数,并返回布尔值。
- size() # 返回 deque 中的项数。它不需要参数,并返回一个整数。
class Deque():
def __init__(self):
self.items = []
def addFront(self, item): # 栈
self.items.append(item)
def addRear(self, item): # 队列
self.items.insert(0, item)
def isEmpty(self):
return self.items == []
def removeFront(self):
if self.isEmpty():
return None
else:
return self.items.pop()
def removeRear(self):
if self.isEmpty():
return None
else:
return self.items.pop(0)
def size(self):
return len(self.items)
案例: 回文检测
- 回文检测:设计程序,检测一个字符串是否为回文。
- 回文:回文是一个字符串,读取首尾相同的字符,例如,radar toot madam。
- 分析:该问题的解决方案将使用 deque 来存储字符串的字符。我们从左到右处理字符串,并将每个字符添加到 deque 的尾部。在这一点上,deque 像一个普通的队列。然而,我们现在可以利用 deque 的双重功能。 deque 的首部保存字符串的第一个字符,deque 的尾部保存最后一个字符。我们可以直接删除并比较首尾字符,只有当它们匹配时才继续。如果可以持续匹配首尾字符,我们最终要么用完字符,要么留出大小为 1 的deque,取决于原始字符串的长度是偶数还是奇数。在任一情况下,字符串都是回文。
def isHuiWei(s):
flag = False
# 将字符添加到双端队列中
q = Dequeue()
# 表示字符串是否为回文
for ch in s:
q.addFront(ch)
while q.size() > 1:
first = q.removeFront()
last = q.removeRear()
if first != last:
return flag
return True
print(isHuiWei('aba'))