数据结构(Python语言描述)- 队列

目录

1 队列概览

2 队列接口

3 队列的2种应用

3.1 模拟

3.2 轮询CPU调度

4 队列的实现

4.1 队列的链表实现

4.2 队列的数组实现

4.3 两种实现的时间和空间复杂度分析

5 案例学习1:模拟超市排队结账

5.1 要求

5.2 分析

5.3 用户界面

5.4 类及其作用

6 优先队列

7 案例学习2:急症室调度

7.1 需求

7.2 分析

7.3 类的设计和实现

8 总结


1 队列概览

  • 队列是线性的集合
  • 队列的插入限制在队尾,删除限制在队头。支持先进先出协议( FIFIO, first-in first-out )
  • 两个基本操作
    • add:在队尾添加一项
    • pop:从队头弹出一项
  • 优先集合:
    • 在优先队列中,具有较高优先级的项,会在那些具有较低优先级的项之前弹出。具有相同优先级的项,则仍然按照 FIFO的顺序弹出
  • 计算机科学中的大多数队列,涉及对共享资源的访问
    • CPU访问——进程排队等待访问一个共享的CPU
    • 磁盘访问——进程排队等待访问共享的辅助存储设备
    • 打印机访问——打印任务排队等待访问共享的激光打印机

2 队列接口

  • Python程序员可以使用一个Python列表来模拟队列。最简单的策略是使用列表的append方法来把项添加到队列的队尾,并且使用pop(0)方法从列表的前面删除项并返回它
  • 该方法的主要缺点是:所有其他的列表操作也都可以操作队列。这包括在任何的位置插入、替换和删除一项。这些操作违反了队列作为一种抽象数据模型的本意
队列接口中的方法

          队列方法

作用
q.isEmpty() 如果q为空返回True,否则返回False
__len__(q) 和len(q)相同,返回q中的项的数目
__str__(q) 和str(q)相同,返回q的字符串表示
q.__iter__() 和iter(q)或for item in q:相同;从前到后,访问q中的每一项
q.__contains__(item) 和item in q相同,如果item在q中,返回True,否则返回False
q1.__add__(q2) 和q1+q2相同,返回一个新的队列,其中包含了q1和q2中的项
q.__eq__(anyObject) 和q==anyObject相同,如果q等于anyObject,返回True,否则返回False。如果队列中对应位置的项都是相同的,则两个队列相等
q.clear() 将q清空
q.peek() 返回q队头的项。先验条件:q不能为空,如果队列为空的话,将会抛出一个KeyError
q.add(item) 将item添加到q的队尾
q.pop() 删除并返回q队头的项。先验条件:q不能为空。如果队列为空的话,将会抛出一个KeyError
  • pop和peek有一个重要的先验条件,并且如果队列的用户不能满足这个先验条件的话,将会引发一个异常
队列操作的效果
操作 该操作之后栈的状态 返回值 说明
Q = ()     初始化,队列为空
q.add(a) a   队列只包含一个项a
q.add(b) a b   a位于队列队头,b位于队列队尾
q.add(c) a b c  

c位于队列队尾

q.isEmpty() a b c False 这个队列不为空
len(q) a b c  3 队列中包含3个项
q.peek() a b c a 返回队头的值,但并不删除它
q.pop() b c a 删除并返回队头的值a,b现在成为队头
q.pop() c b 删除并返回队头的值b,c现在成为栈顶
q.pop()   c 删除并返回队头的值c
q.isEmpty()   True 队列现在为空
q.peek()   KeyError 查看一个空的队列会导致一个异常
q.pop()   KeyError

弹出一个空的队列会导致一个异常

q.add(d) d   d是队头的项

3 队列的2种应用

  • 队列的两个应用,一个设计计算机模拟,另一个设计CPU调度的轮询。

3.1 模拟

  • 确定每天不同时间段,在岗工作的收很员的数目。这种情况下,一些重要的因素如下:
    • 新增顾客的频率
    • 可用的收银员的数目
    • 顾客的购物车中的商品的数目
    • 需要考虑的时间段
  • 这些因素也可以输入到一个模拟程序中,程序随后确定要接待的顾客的数目、每位顾客等待服务的平均时间,以及在模拟时间段的最后还在排队的顾客的数目。通过改变输入,特别是顾客到达的频率和可用的收银员的数目,模拟程序就能够帮助经理在每天的繁忙时段和空闲时段做出有效的人手调配决策。通过添加一个输入来量化不同的结账设备的效率,经理甚至可以判断增加更多的收银员还是购买更多、更好的高效设备更划算。

3.2 轮询CPU调度

  • 轮训调度,就是将新的进程添加到一个等待队列的队尾,这个队列包含了等待使用的CPU的进程
  • 等待队列中的每一个进程都会依次弹出,并且会给予CPU时间的一个分片。当时间分片用完的时候,该进程就会返回到队列的队尾
  • 示意图

数据结构(Python语言描述)- 队列_第1张图片

  • 通常,并不是所有的进程需要CPU的紧急程度都是相同的。例如,用户对于计算机的满意度,在很大程度上受到了计算机对键盘和鼠标输入的响应时间的影响。因此,优先响应处理这些输入的进程是有意义的
  • 轮训调度使用优先队列并且给每个进程分配一个合适的优先级,从而满足这种需求 

4 队列的实现

4.1 队列的链表实现

  • 为了实现对队列两端的快速访问,需要指向两端的外部指针

数据结构(Python语言描述)- 队列_第2张图片

  • add操作:程序创建了一个新的节点,并将最后一个节点的next指针设置为这个新的节点,并且将变量rear设置为这个新节点

数据结构(Python语言描述)- 队列_第3张图片

  • add方法代码: 
def add(self, newItem):
    newNode = Node(newItem, None)
    if self.isEmpty():
        self._front = newNode
    else:
        self._rear.next = newNode
    self._rear = newNode
    self._size += 1
  • pop操作:在一次弹出操作之后,队列变为空,那么必须将front和rear指针都设置为None
  • pop方法代码:
def pop(self):
    """Removes and returns the item at front of the queue.
       Precondition: the queue is not empty."""
    # Check Precondition here
    if self.isEmpty():
        raise KeyError("The queue is empty")
    oldItem = self._front.data
    self._front = self._front.next
    if self._front is None:
        self._rear = None
    self._size -=1
    return oldItem

4.2 队列的数组实现

  •  使用 front 和 rear 两个索引来指示队列的开始和结束位置

数据结构(Python语言描述)- 队列_第4张图片

  • 调整数组大小时,最好让数组占据最初的数组段

4.3 两种实现的时间和空间复杂度分析

  • __str__,__add__ 和 __eq__方法的运行时间是 O(n),其他方法的最大运行时间为O(1)。但是数组实现,在需要调整数组大小时,其运行时间为O(n)
  • 当数组实现的填充因子大于 1/2 时,其内存利用率要比链表实现高,对于小于1/2的装载因子,数组的内存使用效率要低一些

5 案例学习1:模拟超市排队结账

5.1 要求

  • 编写一个程序,允许用户预测一家超市在各种情况下的结账排队的状态

5.2 分析

为了简单起见,我们添加如下的一些限制:

  1. 一个收银员只负责一个结账队列
  2. 每位顾客拥有相同数目的商品等待结账,并且需要相同的服务时间
  3. 新顾客到达结账台的概率不会随着时间而变化

模拟程序的输入如下:

  1. 模拟程序需要的运行的总时间(表示为多少分钟)
  2. 为单个的顾客提供服务所需的分钟数
  3. 在下一分钟,新顾客到达收款台队列的概率。这个概率可能是大于0且小于或等于1的一个浮点数

模拟程序的输出如下:

  1. 所服务的顾客的总数
  2. 队列中剩下的顾客的数目
  3. 每位顾客的平均等待时间
超市结账模拟程序的输入和输出
输入 输入值的范围 输出
总的分钟数 0 <= total <= 1000 要服务的总的顾客数目
每位顾客的平均时间 0 < average <= total 队列中剩下的顾客数目
在下一分钟新到顾客的可能性 0 < probability <= 1 平均等待时间

5.3 用户界面

Welcome to the Market Simulator

Enter the total running time: 60
Enter the average time per customer: 3
Enter the probability of a new arrival: 0.25
TOTALS FOR THE CASHIER
Number of customers served: 16
Number of customers left in queue: 1
Average time customers spend
Waiting to be served: 2.38

5.4 类及其作用

模型中的类
作用
MarketModel

一个超市模型做如下的事情:

1. 运行模拟程序;

2. 创建一个收银员对象;

3. 给收银员发送新的顾客;

4. 维护一个抽象的时钟;

5. 在时钟的每一次滴答过程中,告诉收银员为顾客提供一个单位的服务

Cashier

收银员对象做如下的事情:

1. 包含了顾客对象的一个队列;

2. 当收到指示的时候,将新的对象添加到这个队列中;

3. 依次从队列中删除顾客;

4. 当接到指示的时候,为当前顾客提供一个单位的服务,并且当服务完成后,释放该顾客

Customer

顾客对象:

1. 知道其他的到达时间以及需要多少服务;

2. 当收银员提供服务的时候能够意识到这一点。这个类会根据新到达顾客的概率,作为整体来生成一个新的顾客(当被告知这么做的时候)

LinkedQueue 收银员用它来表示一队顾客
  • 类之间的关系和整体设计

数据结构(Python语言描述)- 队列_第5张图片

  • 由于结账条件已经限定了,MarketModel类的设计相当简单。其构造方法(Constructor)做如下的事情:
    • 1. 保存输入——新顾客的到达概率、模拟的时间长度以及每个顾客的平均服务时间
    • 2. 创建一个单个的收银员
  • 唯一所需的其他方法是runSimulation。该方法运行抽象的时钟,该时钟会驱动结账过程。在每一次时钟滴答的时候,该方法都会做3件事:
    • 1. 要求Customer类产生一个新的顾客,这可能会也可能不会根据新到达的顾客的概率以及一个随机数生成器的输出来产生
    • 2. 如果产生了一个新的顾客,将这个新的顾客发送给收银员
    • 3. 告诉收银员,为当前的顾客提供一个单位的服务
  • 当模拟结束的时候,runSimulation方法将收银员的结果返回给视图。伪代码:
for each minute of the simulation
    ask the Customer class to generate a new customer
    if a customer is generated
        cashier.addCustomer(customer)
        cashier.serveCustomers(current time)
return cashier's results
  • 由于顾客可能在任何给定的时间到来,偶尔情况下,并不会生成顾客。我们可以在Customer类的一个类方法中编写进行这一选择的逻辑代码,而不是将这些代码放在生成顾客的时候
  • 从模型的角度来看,Customer类方法generateCustomer接受了新顾客到达的概率、当前的时间以及每位顾客所需的平均服务时间。该方法使用这些信息来判断是否创建一个顾客,以及如果要创建,如何初始化该顾客。该方法返回的一个新的Customer对象或者是只None。
  • 如下是MarketModel类的代码:
# -*- encoding: utf-8 -*-
# Author: ZFT

from Data_structure.Chapter8.Market.cashier import Cashier
from Data_structure.Chapter8.Market.customer import Customer

class MarketModel(object):

    def __init__(self, lengthOfSimulation, averageTimePerCus, probabilityOfNewArrival):

        self._probabilityOfNewArrival = probabilityOfNewArrival
        self._lengthOfSimulation = lengthOfSimulation
        self._averageTimePerCus = averageTimePerCus
        self._cashier = Cashier()

    def runSimulation(self):
        """Run the clock for n ticks."""
        for currentTime in range(self._lengthOfSimulation):
            # Attempt to generate a new customer
            customer = Customer.generateCustomer(self._probabilityOfNewArrival,\         
                                            currentTime, self._averageTimePerCus)

            # Send customer to cashier if successfully generated
            if customer != None:
                self._cashier.addCustomer(Customer)

            # Tell cashier to provide another unit of service
            self._cashier.serveCustomers(currentTime)

    def __str__(self):
        return str(self._cashier)
  • 收银员负责为一队顾客服务。在这个过程中,收银员会记录接受服务的顾客以及他妈妈们在队列中等待的时间。在模拟的最后,该类的__str__方法返回这些信息以及队列中剩下的顾客的数目。
  • 这个类的实例变量如下:
    • totalCustomerWaitTime——所有顾客的等待时间
    • customerServed——接受服务的顾客数
    • queue——队列中剩下的顾客的数目
    • currentCustomer——当前在接受服务的顾客
  • 为了允许超市模型给收银员发送一个新的顾客,这个类实现了addCustomer方法,该方法接收一个顾客作为参数,并且将顾客添加到收银员的队列中
  • serveCustomers方法在一次时钟滴答中处理收银员的活动。该方法接收当前的时间作为参数,并且可以通过不同的方式来做出响应。
在一次时钟滴答的时候,收银员做出的响应
条件 含义 所采取的操作
当前顾客为None并且队列为空 没有顾客等待服务 什么也不做,直接返回
当前顾客为None并且队列不为空 有一个顾客在队头等待服务

1. 弹出一个顾客使其成为当前顾客

2. 询问他是何时实例化的,以确定他等了多长时间,并且将这个时间加到所有顾客等待的总时间中

3. 将服务的顾客的数目加1

4. 给该顾客一个单位的服务,并且在服务完成的时候遣散该顾客

当前顾客不为None 为当前顾客服务 给该顾客一个单位的服务,并且在服务完成的时候遣散该顾客
  • 如下是serveCustomers方法的伪代码:
if currentCustomer is None
    if queue is empty
        return
    else
        currentCustomer = queue.pop()
        totalCustomerWaitTime = totalCustomerWaitTime + currentTime -     
                                currentCustomer.arrivalTime()
        increment customersServed
            currentCustomer.serve()
            if currentCustomer.amountOfServiceNeeded() == 0
                currentCustomer = None
  • 如下是Cashier类的代码: 
# -*- encoding: utf-8 -*-
# Author: ZFT

from Data_structure.Chapter8.linkedqueue import LinkedQueue

class Cashier(object):

    def __init__(self):

        self._totalCustomerWaitTime = 0
        self._customersServed = 0
        self._currentCustomer = None
        self._queue = LinkedQueue()

    def addCustomer(self):
        self._queue.add()

    def serveCustomers(self, currentTime):
        if self._currentCustomer is None:
            # No customers yet
            if self._queue.isEmpty():
                return
            else:
                # Pop first waiting customer and tally results
                self._currentCustomer = self._queue.pop()
                self._totalCustomerWaitTime += currentTime - self._currentCustomer.arrivalTime()
                self._customersServed += 1

        # Give a unit of service
        self._currentCustomer.serve()

        # If current customer is finished, send it away
        if self._currentCustomer.amountOfServiceNeeded() == 0:
            self._currentCustomer = None


    def __str__(self):
        result = "TOTALS FOR THE CASHIER\n" +\
            "Number of customers served:" +\
            str(self._customersServed) + "\n"
        if self._customersServed != 0:
            aveWaitTime = self._totalCustomerWaitTime / self._customersServed
            result += "Number of customers left in queue:"+\
                str(self._queue) + "\n" +\
                "Average time customers spend\n" +\
                "waiting to be served:" + "%5.2f" %aveWaitTime
        return result

  • Customer类保存了一个顾客的到达时间以及他所需要的服务的数量。其构造方法使用超市模型所提供的数据来初始化这些变量。包含的实例方法如下:
    • arriveTime()——返回顾客到达收银员的队列的时间
    • amountOfServiceNeeded()——返回剩下的服务单位的数目
    • serve()——将服务的单位的数目减少1
  • 剩下的generateCustomer方法,是一个类方法。它接收一个新的顾客到达的概率、当前的时间、每位顾客所需的服务时间单位的数目作为参数。如果概率大于或等于0到1之间的一个随机数的话,该方法返回了具有给定的时间和服务单位的一个Customer的新实例。否则的话,该方法返回None,表示没有产生顾客。
  • 如下是Customer类的代码:
# -*- encoding: utf-8 -*-
# Author: ZFT

import random

class Customer(object):

    @classmethod
    def generateCustomer(cls, probabilityOfNewArrival, arrivalTime, averageTimePerCustomer):
        """Returns a Customer object if the probability of arrival is greater than
           or equal to a random number. Otherwise, returns None, indicating no new customer."""
        if probabilityOfNewArrival >= random.random():
            return Customer(arrivalTime, averageTimePerCustomer)
        else:
            return None

    def __init__(self, arrivalTime, serviceNeeded):
        
        self._arrivalTime = arrivalTime
        self._amountOfServiceNeeded = serviceNeeded
        
    def arrivalTime(self):
        return self._arrivalTime
    
    def amountOfServiceNeeded(self):
        return self._amountOfServiceNeeded
    
    def serve(self):
        """Accepts a unit of service from the cashier."""
        self._amountOfServiceNeeded -= 1

6 优先队列

  • 优先队列是一种特殊的队列类型
  • 当向一个优先队列中添加项的时候,它们都被赋予了一个排列顺序
  • 当删除项的时候,较高优先级的项会在那些较低优先级的项之前删除。相同优先级的项,按照通常的FIFO的顺序删除。
  • 如果A
优先队列的生命周期状态
操作 操作后队列的状态 返回的值 说明
q = ()     最初队列为空
q.add(3) 3   队列中包含了单个的项3
q.add(1) 1 3   1在队头,3在队尾,因为1具有更高的优先级
q.add(2) 1 2 3   2加入队列,但是它的优先级比3高,因此2放到了3的前面
q.pop() 2 3 1 从队列中删除队头的项并返回它,2现在是队头的项了
q.add(3) 2 3 3   新的3插入到了已有的3的后面,按照FIFO的顺序
q.add(5) 2 3 3 5   5的优先级最低,因此它放到了队尾
  • 当一个对象本身是不可比较的时候,可以使用另一个可比较的对象的优先级来包装它。用来将一个不能比较的对象构建为一个可比较对象的包装器类叫作Comparable。
  • 这个类包含了一个构造方法,该方法接收一个项及其优先级作为参数。优先级必须是一个整数、一个字符串或者能够识别比较运算符的一个对象
  • Python在使用比较运算符的时候,会查找对象的比较方法。当创建了包装器对象之后,getItem、getpriority、__str__、__eq__、__len__和__lt__方法被用来提取该项或者其优先级、返回字符串表示以及支持基于优先级的比较。
  • 如下是Comparabale类的代码:
# -*- encoding: utf-8 -*-
# Author: ZFT

class Comparable(object):
    """Wrapper class for items that are not comparable."""
    
    def __init__(self, data, priority = 1):
        self._data = data
        self._priority = priority
        
    def __str__(self):
        """Returns the string representation of the contained datum."""
        return str(self._data)
    
    def __eq__(self, other):
        """Returns True if the contained priorities are equal or False otherwise."""
        if self is other:
            return True
        if type(self) != type(other):
            return False
        return self._priority == other._priority
    
    def __lt__(self, other):
        """Returns True if self's priority < other's priority, or False otherwise."""
        return self._priority < other._priority
    
    def __le__(self, other):
        """Returns True if self's priority <= other's priority, or False otherwise."""
        return self._priority <= other._priority
    
    def getData(self):
        """Returns the contained datum."""
        return self._data
    
    def getPriority(self):
        """Returns the contained priority."""
        return self._priority
        
  • 优先队列的有序列表的实现
  • 有序的列表是以一种自然的顺序维护的可比较项的一个列表。优先队列列表应该按照这样的顺序排列,总是只能够从列表的一端访问或删除最小的元素。元素按照顺序插入正确的位置。
  • 如果总是从结构的头部删除最小的元素的话,单链表结构可以很好地表示这种类型的列表。如果这个结构继承来自LinkedQueue类中使用的单链表结构,我们可以通过运行该方法的pop方法来继续删除一个元素。只有add方法需要做出修改。在新的名为LinkedPriorityQueue的子类中,需要覆盖其定义。
  • add方法的新的实现要对新项在列表中的位置进行搜索。它考虑到如下的情况:
    • 如果队列为空,或者新的项大于或者等于队尾的项,那么就像之前一样添加该项(它将会添加到队尾)
    • 否则,从队头开始向前开始遍历各个节点,直到新项小于当前节点中的项。此时,必须在当前节点和其之前的节点(如果之前有节点的话)之间,插入一个新的节点以包含该项。为了完成这一插入,搜索使用了两个指针,分别是probe和trailer。当搜索停止的时候,probe指向了新项的位置之后的节点。新的节点的next指针随后设置为probe指针。之前的节点的next指针随后重置为指向新的节点(如果probe没有指向第一个节点的话),否则,将队列的front指针设置为新的节点。
  • LinkedPriorityQueue类的代码如下:
# -*- encoding: utf-8 -*- 
# Author: ZFT

from node import Node
from linkedqueue import LinkedQueue

class LinkedPriorityQueue(LinkedQueue):
    """A link-based priority queue implementation."""

    def __init__(self, sourceCollection = None):
        """Sets the initial state of self, which includes the 
           contents of sourceCollection, if it's present."""
        LinkedQueue.__init__(self, sourceCollection)

    def add(self, newItem):
        """Inserts newItem after items of greater or equal priority or ahead
           of items of lesser priority. A has greater priority than B if A < B."""
        if self.isEmpty() or newItem >= self._rear.data:
            # New items goes at rear
            LinkedQueue.add(self, newItem)
        else:
            # Search for a position where it's less
            probe = self._front
            while newItem <= probe.data:
                trailer = probe
                probe = probe.next
            newNode = Node(newItem, probe)
            if probe = self._front:
                # New Item goes at front
                self._front = newNode
            else:
                # New Item goes between two nodes
                trailer.next = newNode
        self._size += 1            
          

7 案例学习2:急症室调度

7.1 需求

  • 编写一个程序,允许管理者调度对医院急症室的病人的治疗。假设由于一些病人的情况会比另一些病人更为严重,所以不会要个按照先来先治疗的顺序来治疗病人。当接收病人的时候,会指定一个优先级,具有较高优先级的病人会在具有较低优先级的病人之前接受治疗。

7.2 分析

  • 来到急症室的病人分为3种情况。按照优先级顺序,可以这样排列他们的情况:
    • 危重
    • 严重
    • 一般
  • 当用户选择了调度选项的时候,程序允许用户输入病人的名称和情况,并会根据病人的情况,将其放到相应的治疗队列中
  • 当用户选择Treat Next Patient选项时,程序会删除并显示情况最严重的队列中的第一个病人
  • 当用户选择Treat All Patients选项时,程序会依次删除并显示所有的病人——从第一个接受治疗的知道最后一个接受治疗的
  • 每一个命令按钮都会在输出区域显示出一条相应的消息
急症室程序的命令
用户命令 程序响应
Schedule 提示用户输入病人的名字和情况,然后打印、添加到队列的
Treat Next Patient 打印出将要治疗的
Treat All Patients 打印出将要治疗的——打印出将要治疗的
  • 如下是基于终端界面的一次交互:
Main Menu
 1 Schedule a patient
 2 Treat the next patient
 3 Treat all patients
 4 Exit the program

Enter a number [1-4]: 1

Enter the patient's name: Bill
Patient's condition:
 1 Critical
 2 Serious
 3 Fair

Enter a number [1-3]: 1
Bill is added to the critical list

Main Menu
 1 Schedule a patient
 2 Treat the next patient
 3 Treat all patients
 4 Exit the program

Enter a number [1-4]: 3

Bill / critical is being treated.
Martin / serious is being treated.
Ken / fair is being treated.
No patients available to treat.

7.3 类的设计和实现

  • 该应用程序包含一个名为ERView的视图类和一组模型类。视图类和用户交互,并且通过模型来运行方法。
  • ERModel类维护了病人的一个优先队列
  • Patient类表示病人
  • Condition类表示3种可能的情况
  • Patient和Condition类维护了一个病人的名称和情况。可以将其作为字符串(根据病人的情况)并查看。如下是两个类的代码:
# -*- encoding: utf-8 -*-
# Author: ZFT

class Condition(object):
    
    def __init__(self, rank):
        self._rank = rank

    def __ge__(self, other):
        """Used for comparison."""
        return self._rank >= other._rank

    def __str__(self):
        if self._rank == 1:
            return "Critical"
        elif self._rank == 2:
            return "Serious"
        else:
            return "Fair"

class Patient(object):
    
    def __init__(self, name, condition):
        self._name = name
        self._condition = condition

    def __ge__(self, other):
        """Used for comparison."""
        return self._condition >= other._condition

    def __str__(self):
        return self._name + "/" + str(self._condition)
  • ERView类使用一个典型的菜单——驱动循环。通过几个辅助方法来构造代码。如下是完整的列表:
# -*- encoding: utf-8 -*-
# Author: ZFT

"""
The view for an emergency room scheduler.
"""

from model import ERModel, Patient, Condition

class ERView(object):
    """The view class for the ER application."""

    def __init__(self, model):
        self._model = model
       
    def run(self):
        """Menu-driven command loop for the app."""
        
        menu = "Main Menu\n" + \
               "1 Schedule a patient\n" + \
               "2 Treat the next patient\n" + \
               "3 Treat all patients\n" + \
               "4 Exit the program\n"
        while True:
            command = self._getCommand(4, menu)
            if command == 1:
                self._schedule()
            elif command == 2:
                self._treatNext()
            elif command == 3:
                self._treatAll()
            else:
                break

    def _treatNext(self):
        """Treats one patient if there is one."""
        if self._model.isEmpty():
            print("No patients available to treat.")
        else:
            patient = self._model.treatNext()
            print(patient, "is being treated.")

    def _treatAll(self):
        """Treats all the remaining patients."""
        if self._model.isEmpty():
            print("No patients available to treat.")
        else:
            while not self._model.isEmpty():
                self.treatNext()

    def _schedule(self):
        """Obtains patient info and schedules patient."""
        name = input("\nEnter the patient's name:")
        condition = self._getCondition()
        self._model.schedule(Patient(name, condition))
        print(name, "is added to the", condition, "list\n")

    def _getCondition(self):
        """Obtains condition info."""
        menu = "Patient's condition:\n" + \
               " 1 Critical\n" + \
               " 2 Serious\n" + \
               " 3 Fair\n"
        number = self._getCommand(3, menu)
        return Condition(number)

    def _getCommand(self, high, menu):
        """Obtains and returns a command number."""
        prompt = "Enter a number [1-" + str(high) +"]"
        commandRange = list(map(str, range(1, high + 1)))
        error = "Error, number must be 1 to" + str(high)
        while True:
            print(menu)
            command = input(prompt)
            if command in commandRange:
                return int(command)
            else:
                print(error)


    # Main function to start up the application
    def main():
        model = ERModel()
        view = ERView(model)
        view.run()

    if __name__ == "__main__":
        main()
         

8 总结

  1. 队列是一个线性的集合,它在一端(队尾)添加元素,并且从另一端(队头)删除元素。因此,元素按照先进先出(first in first out, FIFO)的顺序访问
  2. 队列上的其他操作包括:查看顶部的元素、确定元素的编号、判断队列是否为空,以及返回一个字符串表示
  3. 在应用程序中,队列按照FIFO的顺序管理数据项。这些应用程序包括调度要处理的项,或者调度对资源的访问
  4. 数组和单链表结构都支持队列的简单实现
  5. 优先队列会使用一个排序方案和FIFO顺序来调度其元素。如果两个元素具有相同的优先级,将会按照FIFO的顺序调度它们。否则,元素根据某些属性,例如数字或字母内容,按照从大到小的顺序排列。通常,具有最小优先级的值的元素会被先删除,而不管它们是何时添加到优先队列中的

你可能感兴趣的:(数据结构与算法)