【数据结构与算法】队列及Python实现

1 队列抽象数据类型及Python实现

  • 定义: 当数据项加入队列,首先出现在队尾,随着队首数据项的移除,它逐渐接近队首。

在这里插入图片描述

  • 特性: 队列是一种有次序的数据集合,新数据项的添加总发生在一端(通常称为 “ 尾rear ” 端),而现存数据项的移除总发生在另一端(通常称为“ 首front ” 端)。

1.1 抽象数据类型Queue

  • Queue() :创建一个空队列对象,返回值为Queue 对象;
  • enqueue(item) :将数据项item 添加到队尾,无返回值;
  • dequeue() :从队首移除数据项,返回值为队首数据项,队列被修改;
  • isEmpty() :测试是否空队列,返回值为布尔值
  • size():返回栈中有多少个数据项

【操作样例】

Stack OperationStack ContentsReturn Value
q.isEmpty()[]True
q.enqueue(4)[4]
q.enqueue(‘dog’)[‘dog’,4]
q.enqueue(True)[True,‘dog’,4]
q.size()[True,‘dog’,4]3
q.isEmpty()[True,‘dog’,4]False
q.enqueue(8.4)[8.4,True,‘dog’,4]
q.dequeue()[8.4,True,‘dog’]4
q.dequeue()[8.4,True]‘dog’
q.size()[8.4,True]2

1.2 Python实现ADT Queue

class Queue:
    def __init__(self):
        self.items = []

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

    def enqueue(self, item):
        """进入队列,复杂度O(n)"""
        self.items.insert(0, item)

    def dequeue(self):
        """出队列,复杂度O(1)"""
        return self.items.pop()

    def size(self):
        """队列大小"""
        return len(self.items)

2 队列的应用

2.1 热土豆(约瑟夫问题)算法

问题描述:

小孩围成一个圈,将土豆依次传递给相邻的人,在某个时间点,游戏暂停并且手里持有土豆的人出局,游戏继续直至剩下一个人。

在这里插入图片描述

算法

  • 用队列来实现热土豆问题的算法,参加游戏的人名列表,以及传土豆次数num,算法返回最后剩下的人名

  • 模拟程序采用队列来存放所有参加游戏的人名,按照传递土豆方向从队首排到队尾,游戏时,队首始终是持有土豆的人

  • 模拟游戏开始,只需要将队首的人出队,随即再到队尾入队,算是土豆的一次,传递传递了num 次后,将队首的人移除,不再入队如此反复,直到队列中剩余1

在这里插入图片描述

【代码】

class Queue:...

def hotPotato(namelist, num):
    simqueue = Queue()
    for name in namelist:
        simqueue.enqueue(name)

    while simqueue.size() > 1:
        for i in range(num):
            simqueue.enqueue(simqueue.dequeue())
        simqueue.dequeue()

    return simqueue.dequeue()


print(hotPotato(["Bill", "David", "Susan", "Jane", "Kent", "Brad"], 6))		# Kent

2.2 打印任务算法

题目描述: 多人共享一台打印机。在一个实验室内,每一个小时内,平均在场10名学生,每名学生在这一个小时内平均发起两次打印,每次打印的1~20页(概率相同)。打印机打印模式有两种,以草稿模式打印,每分钟打印10页;以标准模式打印(打印质量较好),每分钟打印5页。那么怎么设定打印机的模式,使得大家在不会等太久的情况下提升打印质量?

题目分析: 这种题可以看出来是不能直接给出结论的,需要一次次模拟程序,不断执行,通过对比结果而得出最终结论。首先来简单看一下题目,分析几个关键点。问题是如何设置打印机的模式,使得不会等太久,并且打印质量高。

  • 打印机的模式:标准模式和草稿模式,这两种模式其实是对应了打印机的速度和打印质量。

  • 使得大家不会等太久:也就是大家的等待时间计算问题。等待时间,就是你提交了任务开始起(从这里开始等待),到打印机开始打印(打印机开始打印你的任务)。你提交了任务起,也就是你生成文档任务的时间,打印开始打印就是打印机开始下一个任务。而生成文档任务,也有学生人数和每人生成文档的概率等要素,这里需要把生成的任务,放入队列,而开始打印取决于打印机的状态,是空闲还是忙,打印过程就是时间流逝,当然也取决于文档的页数,也是概率问题。

  • 打印质量高:其实就是打印机的模式问题,速度问题。和第一点一样。
    通过上述分析可以抽取出几个属性。打印机的打印速度,当前状态(是否有任务),打印过程,开始打印任务;任务的生成时间,任务的页数,任务的等待时间;任务生成。因此可以模拟一个小时的打印过程,输出结果
    所以可以大致抽象出打印机和任务两个对象。打印机,有打印速度,当前打印任务和剩余打印时间三个属性,打印过程,当前状态和开始打印三个方法。

【代码】

import random

class Queue:...

class Printer(object):
    def __init__(self, ppm):
        """初始化打印的打印模式"""
        self.pagerate = ppm         # 打印速度,每分钟多少页
        self.currentTask = None     # 当前状态,是否有任务
        self.timeRemaining = 0      # 如果有任务,当前任务剩余时间

    def tick(self):
        """打印过程"""
        if self.currentTask is not None:
            self.timeRemaining -= 1         # 如果当前有任务,则进行打印(打印1秒)
            if self.timeRemaining <= 0:     # 并计算任务剩余时间
                self.currentTask = None

    def busy(self):
        """打印机是否忙"""
        if self.currentTask is not None:
            return True
        else:
            return False

    def startNext(self, newtask):
        """开始打印下一个任务,计算任务剩余时间,这里按秒进行计算,需要进行单位换算"""
        self.currentTask = newtask
        self.timeRemaining = newtask.getPages() * 60 / self.pagerate


class Task(object):
    """
    初始化任务生成时间
    同等概率,随机指定任务的页数1-20页
    """
    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():
    """20个任务 / 3600秒, 平均每秒产生1/180个任务"""
    num = random.randrange(1, 181)
    if num == 180:
        """每秒 1/180 概率产生新任务"""
        return True
    else:
        return False


def simulation(newSeconds, pagePerMinute):
    labprinter = Printer(pagePerMinute)
    printQueue = Queue()
    waitingtimes = []

    # 时间流逝
    for currentSecond in range(newSeconds):
        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()       # 若有任务,打印1秒

    averageWait = sum(waitingtimes)/len(waitingtimes)

    print('Average Wait {:6.2f} secs {:3d} tasks remaining.'.format(averageWait, printQueue.size()))


# 1 小时, 每分钟打印 10 页
for i in range(10):
    simulation(3600, 10)

Average Wait  20.28 secs   0 tasks remaining.
Average Wait  22.43 secs   0 tasks remaining.
Average Wait   8.47 secs   0 tasks remaining.
Average Wait  15.77 secs   0 tasks remaining.
Average Wait  28.85 secs   0 tasks remaining.
Average Wait   0.47 secs   0 tasks remaining.
Average Wait   7.73 secs   0 tasks remaining.
Average Wait  11.82 secs   0 tasks remaining.
Average Wait  23.21 secs   0 tasks remaining.
Average Wait  19.95 secs   0 tasks remaining.

# 1 小时, 每分钟打印 5 页
for i in range(10):
    simulation(3600, 5)
    
Average Wait  75.43 secs   0 tasks remaining.
Average Wait  38.35 secs   1 tasks remaining.
Average Wait  44.29 secs   2 tasks remaining.
Average Wait 125.79 secs   1 tasks remaining.
Average Wait 308.58 secs   3 tasks remaining.
Average Wait  69.22 secs   0 tasks remaining.
Average Wait  27.62 secs   0 tasks remaining.
Average Wait  81.05 secs   0 tasks remaining.
Average Wait  24.33 secs   0 tasks remaining.
Average Wait  91.05 secs   0 tasks remaining.


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值