python数据结构---3.线性数据结构(队列)

前言

在上一篇博客中我提到了线性数据结构,并且讲了第一种线性数据结构栈的特点以及代码实现,那今天就继续来讲讲队列。

今天的主角:队列

队列是有序集合,添加操作发生在“尾部”,移除操作则发生在“头部”。新元素从尾部进入队列,然后一直向前移动到头部,直到成为下一个被移除的元素。最新添加的元素必须在队列的尾部等待,在队列中时间最长的元素则排在最前面。这种排序原则被称作 FIFO(first-in first-out),即先进先出。

在日常生活中,我们经常排队,这便是最简单的队列例子。进电影院要排队,在超市结账要排队,买咖啡也要排队(等着从盘子栈中取盘子)。好的队列只允许一头进,另一头出,不可能发生插队或者中途离开的情况;计算机科学中也有众多的队列例子。假如计算机实验室有 30 台计算机,它们都与同一台打印机相连。当学生需要打印的时候,他们的打印任务会进入一个队列。该队列中的第一个任务就是即将执行的打印任务。如果一个任务排在队列的最后面,那么它必须等到前面的任务都执行完毕后才能执行。操作系统使用一些队列来控制计算机进程。调度机制往往基于一个队列算法,其目标是尽可能快地执行程序,同时服务尽可能多的用户。在打字时,我们有时会发现字符出现的速度比击键速度慢。这是由于计算机正在做其他的工作。击键操作被放入一个类似于队列的缓冲区,以便对应的字符按正确的顺序显示。

队列抽象数据类型

  • Queue()创建一个空队列。它不需要参数,且会返回一个空队列。
  • enqueue(item)在队列的尾部添加一个元素。它需要一个元素作为参数,不返回任何值。
  • dequeue()从队列的头部移除一个元素。它不需要参数,且会返回一个元素,并修改队列的内容。
  • isEmpty()检查队列是否为空。它不需要参数,且会返回一个布尔值。
  • size()返回队列中元素的数目。它不需要参数,且会返回一个整数。

下面来看看它的python代码实现:

1.队列的python实现

# 这里我们用列表头入队,列表尾出队
class Queue:
    def __init__(self):
        self.items=[]
        
    def isEmpty(self):
        return '队列为空' if self.items==[] else'队列不为空'
    
    def enqueue(self,item):
        self.items.insert(0,item)
    
    def dequeue(self):
        return self.items.pop()
    
    def size(self):
        return len(self.items)
    
q=Queue()
print(q.isEmpty())
q.enqueue('1')
q.enqueue('abc')
q.enqueue(23)
print(q.isEmpty())
print(q.size())
print(q.dequeue())

在这里插入图片描述

2.队列的应用

利用队列解决问题1:著名的约瑟夫斯问题
相传,约瑟夫斯当年和 39 个战友在山洞中对抗罗马军队。眼看着即将失败,他们决定舍生取义。于是,他们围成一圈,从某个人开始,按顺时针方向杀掉第 7 人。约瑟夫斯同时也是卓有成就的数学家。据说,他立刻找到了自己应该站的位置,从而使自己活到了最后。

from pythonds.basic import Queue

def solve(personlist,num):
    q=Queue()
    for person in personlist:
        q.enqueue(person)
    while q.size()>1:
        for i in range(num):
            q.enqueue(q.dequeue())
        q.dequeue()
        
    return q.dequeue()

print(solve([i for i in range(40)],7))

在这里插入图片描述
利用队列解决问题2:打印机问题

考虑计算机科学实验室里的这样一个场景:在任何给定的一小时内,实验室里都有约 10 个学生。他们在这一小时内最多打印 2 次,并且打印的页数从 1 到 20 不等。实验室的打印机比较老旧,每分钟只能以低质量打印 10 页。可以将打印质量调高,但是这样做会导致打印机每分钟只能打印 5 页。降低打印速度可能导致学生等待过长时间。那么,应该如何设置打印速度呢?

思路

  1. 创建一个打印任务队列。每一个任务到来时都会有一个时间戳。一开始,队列是空的。
  2. 针对每一秒(currentSecond),执行以下操作。
    是否有新创建的打印任务?如果是,以 currentSecond 作为其时间戳并将该任务加入到队列中。如果打印机空闲,并且有正在等待执行的任务,执行以下操作:
  • 从队列中取出第一个任务并提交给打印机;
  • 用 currentSecond 减去该任务的时间戳,以此计算其等待时间;
  • 将该任务的等待时间存入一个列表,以备后用;
  • 根据该任务的页数,计算执行时间。
  • 打印机进行一秒的打印,同时从该任务的执行时间中减去一秒。
  • 如果打印任务执行完毕,或者说任务需要的时间减为 0,则说明打印机回到空闲状态。
  1. 当模拟完成之后,根据等待时间列表中的值计算平均等待时间
import random
from pythonds.basic import Queue
class Printer:
    def __init__(self,ppm):
        self.pagerate=ppm
        self.currentTask=None
        self.timeRemaining=0
    
    def tick(self):
        if self.currentTask != None:
            self.timeRemaining=self.timeRemaining-1
            if self.timeRemaining<=0:
                self.currentTask=None
                
    def busy(self):
        if self.currentTask!=None:
            return True
        else:
            return False
        
    def startNext(self,newtask):
        self.currentTask=newtask
        self.timeRemaining=newtask.getPages()*60/self.pagerate
        

class Task:
    def __init__(self,time):
        self.timestamp=time
        self.pages=random.randrange(1,21)
        
    def getStamp(self):
        return self.timestamp
    
    def getPages(self):
        return self.pages

    def waitTime(self,currenttime):
        return currenttime-self.timestamp
    
def newPrintTask():
    num=random.randrange(1,181)
    # 每小时20个任务,相当于180s一个任务
    if num==180:
        return True
    else:
        return False
    
def simulation(numSeconds,pagesPerMinute):
    labprinter=Printer(pagesPerMinute)
    printqueue=Queue()
    waitingtimes=[]
    
    for currentSecond in range(numSeconds):
        if newPrintTask():
            task=Task(currentSecond)
            printqueue.enqueue(task)
        if (not labprinter.busy()) and (not printqueue.isEmpty()):
            nexttask=printqueue.dequeue()
            waitingtimes.append(nexttask.waitTime(currentSecond))
            labprinter.startNext(nexttask)
        labprinter.tick()
    averageWait=sum(waitingtimes)/len(waitingtimes)
    print(f"平均等待时间:{averageWait}s,剩余{printqueue.size()}件工作")
    
for i in range(10):
    simulation(3600,5)
print("--------------------------")
for i in range(10):
    simulation(3600,10)

在这里插入图片描述
可以看到还是每分钟十页比较好。

一个新的朋友:双端队列

双端队列(deque,全名double-ended queue) 是与队列类似的有序集合。它有一前、一后两端,元素在其中保持自己的位置。与队列不同的是,双端队列对在哪一端添加和移除元素没有任何限制。新元素既可以被添加到前端,也可以被添加到后端。同理,已有的元素也能从任意一端移除。某种意义上,双端队列是栈和队列的结合。
在这里插入图片描述
双端队列抽象数据类型

  • Deque() 创建一个空的双端队列
  • addfront(item) 从队头加入一个item元素
  • addrear(item) 从队尾加入一个item元素
  • removefront() 从队头删除一个item元素
  • removerear() 从队尾删除一个item元素
  • isEmpty() 判断双端队列是否为空
  • size() 返回队列的大小

1.双端队列的python实现

class Deque(object):
    """双端队列"""
    def __init__(self):
        self.items = []

    def isEmpty(self):
        """判断队列是否为空"""
        return self.items == []

    def addfront(self, item):
        """在队头添加元素"""
        self.items.insert(0,item)

    def addrear(self, item):
        """在队尾添加元素"""
        self.items.append(item)

    def removefront(self):
        """从队头删除元素"""
        return self.items.pop(0)

    def removerear(self):
        """从队尾删除元素"""
        return self.items.pop()

    def size(self):
        """返回队列大小"""
        return len(self.items)
    
deque = Deque()
deque.addfront(1)
deque.addfront(2)
deque.addrear(3)
deque.addrear(4)
print(deque.items)
print(deque.size())
print(deque.removefront())
print(deque.removefront())
print(deque.removerear())
print(deque.removerear())

在这里插入图片描述

2.双端队列的应用

用双端队列解决回文问题: 回文是指从前往后读和从后往前读都一样的字符串,例如 radar、toot,以及 madam。

from pythonds.basic import Deque 
def palchecker(aString): 
    chardeque = Deque() 
    for ch in aString: 
        chardeque.addRear(ch) 
    stillEqual = True 
    while chardeque.size() > 1 and stillEqual: 
        first = chardeque.removeFront() 
        last = chardeque.removeRear() 
        if first != last: 
            stillEqual = False 
    return stillEqual 

print(palchecker('toot'))
print(palchecker('radar'))
print(palchecker('ndasjkfbjka'))

在这里插入图片描述

日常结尾吐槽

唉,JAVA web真难,也不是难吧就是之前上课没好好听,然后最近该看的网课视频也耽误了没看。一个简单的MVC例子,明明原理都明白,但是写起来总感觉这里一点那里一点没搞清楚,感觉今天一下午写例子的时候其实已经有点感觉,等我周末去补补习应该就问题不大了。最近要学的,要复习的,要做的太多,每天很累,但是还是要坚持!!!(然后这篇博客也没怎么太用心写,这里小声道个歉,对不起)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shelgi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值