队列
队列的定义
队列是有序集合, 添加操作发生在“尾部”, 移除操作则发生在“头部”。 新元素从尾部进入队列, 然后一直向前移动到头部, 直到成为下一个被移除的元素。
最新添加的元素必须在队列的尾部等待, 在队列中时间最长的元素则排在最前面。 这种排序原则被称作 FIFO ( first-in first-out) ,即先进先出, 也称先到先得。
书本上的python实现
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
这个实现显然不好,因为列表的insert的复杂度是O(n)的,这在我们之前的文章中说过了,这导致实现速度非常的慢。而python内部有queue库的实现,这里就进行两者的对比看看。
from pythonds.basic import Queue as q1
from time import time
import queue as q2
q_1 = q1()
q_2 = q2.Queue()
start = time()
for i in range(100000):
q_1.enqueue(i)
end = time()
print(end-start) # 5.54210090637207
start = time()
for i in range(100000):
q_2.put(i)
end = time()
print(end-start) # 0.3688485622406006
因此,我们应该使用python内部实现的,而非本书提供的方式。
queue模块
接下来先简单介绍一下queue.Queue的功能,queue模块其实也有栈的实现(大家可以自行查阅相关资料),但是由于python的append和pop的实现是O(1)的,所以之前的文章就直接这样使用了。
from queue import Queue
q = Queue(maxsize=3) # maxsize表示队列的容量 当maxsize 小于等于 0时 表示无限容量
for i in range(3):
q.put(i) # 往队列中添加元素
print(q.empty()) # 表示队列是否为空
print(q.full()) # 判断队列是否为满
print(q.qsize()) # 返回队列中的元素个数
for j in range(3):
q.get() # 从队列中拿出元素
for i in range(3):
q.put_nowait(i) # 这个也是会往队列添加元素
for i in range(3):
q.get_nowait() # 这个也是从队列中拿东西
### xxx_nowait 和 xxx的区别
"""
先讲结论 一般情况下
在多进程中使用get 和 put
在单进程中使用get_nowait和put_nowait
原因是 get 和 put 当队列没元素时,
他不会报错而是程序持续运行 等待队列更新元素,这显然只有在多进程中可以实现
而get_nowait 和 put_nowait 则不会等待队列更新,而是直接报错,
"""
约瑟夫斯问题
这本书在队列这里提供了两个应用,一个是约瑟夫斯问题还有一个是打印机的问题,由于打印机的问题稍微有点复杂,篇幅写起来就很长了,就先讲一个约瑟夫斯问题,后面专门搞一篇文章讲打印机的问题。
先简单介绍一下约瑟夫斯问题的背景:弗拉维奥·约瑟夫斯是公元 1 世纪著名的历史学家。 相传, 约瑟夫斯当年和 39 个战友在山洞中对抗罗马军队。 眼看着即将失败, 他们决定舍生取义。 于是,他们围成一圈, 从某个人开始, 按顺时针方向杀掉第 7 人。 约瑟夫斯同时也是卓有成就的数学家。 据说, 他立刻找到了自己应该站的位置, 从而使自己活到了最后。 当只剩下他时, 约瑟夫斯加入了罗马军队, 而不是自杀。 这个故事有很多版本, 有的说是每隔两个人,有的说最后一个人可以骑马逃跑。 不管如何, 问题都是一样的。
这个问题怎么处理呢?其实就是使用一个队列,不断的出列,入列,然后通过一个变量去记录1-7的数字,然后数字变七,就出列不入列,直到最后只剩一个人。
书上的那个结果是有问题的,他的结果是6个玩这个游戏,答案是3,下面是我们的画图解法,显然最后的结果是5,所以书上的那个解法是错误的。
代码实现
from queue import Queue
q = Queue()
n = 1
for i in range(1, 41):
q.put(i)
while q.qsize() > 1: # 留到只剩最后一个人为止
if n == 7: # 第七个就重新计数 然后出列一个
n = 0
q.get()
else: # 其他情况就是出列入列
temp = q.get()
q.put(temp)
n += 1 # 变量的记录
print("最终的胜利者的序号为%d" % q.get())
代码浅运算了一下,反正大概就是说40个人,每七个杀一个人,那么最后你选24的位置,你就可以活下来了。
然后下一篇博客就把那个打印任务单独拿出来处理掉