目录
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 总结
队列方法 |
作用 |
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 |
操作 | 该操作之后栈的状态 | 返回值 | 说明 |
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是队头的项 |
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
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
为了简单起见,我们添加如下的一些限制:
模拟程序的输入如下:
模拟程序的输出如下:
输入 | 输入值的范围 | 输出 |
总的分钟数 | 0 <= total <= 1000 | 要服务的总的顾客数目 |
每位顾客的平均时间 | 0 < average <= total | 队列中剩下的顾客数目 |
在下一分钟新到顾客的可能性 | 0 < probability <= 1 | 平均等待时间 |
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
类 | 作用 |
MarketModel | 一个超市模型做如下的事情: 1. 运行模拟程序; 2. 创建一个收银员对象; 3. 给收银员发送新的顾客; 4. 维护一个抽象的时钟; 5. 在时钟的每一次滴答过程中,告诉收银员为顾客提供一个单位的服务 |
Cashier | 收银员对象做如下的事情: 1. 包含了顾客对象的一个队列; 2. 当收到指示的时候,将新的对象添加到这个队列中; 3. 依次从队列中删除顾客; 4. 当接到指示的时候,为当前顾客提供一个单位的服务,并且当服务完成后,释放该顾客 |
Customer | 顾客对象: 1. 知道其他的到达时间以及需要多少服务; 2. 当收银员提供服务的时候能够意识到这一点。这个类会根据新到达顾客的概率,作为整体来生成一个新的顾客(当被告知这么做的时候) |
LinkedQueue | 收银员用它来表示一队顾客 |
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
# -*- 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)
条件 | 含义 | 所采取的操作 |
当前顾客为None并且队列为空 | 没有顾客等待服务 | 什么也不做,直接返回 |
当前顾客为None并且队列不为空 | 有一个顾客在队头等待服务 | 1. 弹出一个顾客使其成为当前顾客 2. 询问他是何时实例化的,以确定他等了多长时间,并且将这个时间加到所有顾客等待的总时间中 3. 将服务的顾客的数目加1 4. 给该顾客一个单位的服务,并且在服务完成的时候遣散该顾客 |
当前顾客不为None | 为当前顾客服务 | 给该顾客一个单位的服务,并且在服务完成的时候遣散该顾客 |
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
# -*- 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
# -*- 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
操作 | 操作后队列的状态 | 返回的值 | 说明 |
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的优先级最低,因此它放到了队尾 |
# -*- 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
# -*- 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
用户命令 | 程序响应 |
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.
# -*- 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)
# -*- 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()