4.4 队列
与栈一样,队列(quene)也是存放数据对象的一种容器,其中的数据对象也按线性的逻辑次序排列。队列结构同样支持对象的插入和删除,但两种操作的范围分别被限制于队列的两端——队尾(rear)和队头(front)。
- 站在队列的角度:元素的插入(insert)与删除(delete)
- 站在元素的角度:入队(enqueue)与出队(dequeue)
规律:“先进先出”(first-in-first-out, FIFO)
ADT接口:
操作实例:
模板类:
既然队列也可视作序列的特里,故只要将队列作为列表的派生类,即可利用C++的继承机制,基于列表模板类,实现队列结构。
0001 #include "List/List.h" //以List为基类派生出的
0002 template <typename T> class Queue: public List<T> { //队列模板类
0003 public: //原有接口一概沿用,如size()、empty()
0004 void enqueue ( T const& e ) { insertAsLast ( e ); } //入队:尾部插入
0005 T dequeue() { return remove ( first() ); } //出队:首部删除
0006 T& front() { return first()->data; } //队首
0007 };
4.5 队列应用
-
循环分配器
为在客户(client)群体中共享的某一资源(比如多个应用程序共享同一CPU),一套公平且高效的分配规则必不可少,而队列结构则非常适于定义和实现这样的一套分配规则。
这里,每位客户持续占用资源的时间,对该算法的成败至关重要:一方面,为保证响应速度,这一时间值通常都不能过大。另一方面,因占有权的切换也需要耗费一定的时间,故若该时间值取得过小,切换过于频繁,又会造成整体效率的下降。因此,往往需要通过实测确定最佳值。
-
银行服务模拟
通常,银行都设有多个窗口,顾客按到达的次序分别在各窗口排队等待办理业务。
这里,首先根据银行所设窗口的数量相应地建立多个队列。以下以单位时间为间隔反复迭代,直至下班。每一时刻都有一位顾客按一定的概率抵达,随机确定所办业务服务时长之后,归入某一“最优”队列。每经单位时间,各队列最靠前顾客(如果有的话)的待服务时长均相应减少一个单位。若时长归零,则意味着该顾客的业务已办理完毕,故应退出队列并由后一位顾客(如果有的话)接替。可见,顾客归入队列和退出队列的事件可分别由enqueue()和dequeue()操作模拟,查询并修改队首顾客时长的事件则可由front()操作模拟。
0001 void simulate ( int nWin, int servTime ) { //按指定窗口数、服务总时间模拟银行业务 0002 Queue<Customer>* windows = new Queue<Customer>[nWin]; //为每一窗口创建一个队列 0003 for ( int now = 0; now < servTime; now++ ) { //在下班之前,每隔一个单位时间 0004 if ( rand() % ( 1 + nWin ) ) { //新顾客以nWin/(nWin + 1)的概率到达 0005 Customer c ; c.time = 1 + rand() % 98; //新顾客到达,服务时长随机确定 0006 c.window = bestWindow ( windows, nWin ); //找出最佳(最短)的服务窗口 0007 windows[c.window].enqueue ( c ); //新顾客加入对应的队列 0008 } 0009 for ( int i = 0; i < nWin; i++ ) //分别检查 0010 if ( !windows[i].empty() ) //各非空队列 0011 if ( -- windows[i].front().time <= 0 ) //队首顾客的服务时长减少一个单位 0012 windows[i].dequeue(); //服务完毕的顾客出列,由后继顾客接替 0013 } //for 0014 delete [] windows; //释放所有队列(此前,~List()会自动清空队列) 0015 }为更好地为新到顾客确定一个队列,这里采用了“最短优先”的原则。如下所示,为此只需遍历所有的队列并通过size()接口比较其规模,即可找到其中的最短者。
0001 int bestWindow ( Queue<Customer> windows[], int nWin ) { //为新到顾客确定最佳队列 0002 int minSize = windows[0].size(), optiWin = 0; //最优队列(窗口) 0003 for ( int i = 1; i < nWin; i++ ) //在所有窗口中 0004 if ( minSize > windows[i].size() ) //挑选出 0005 { minSize = windows[i].size(); optiWin = i; } //队列最短者 0006 return optiWin; //返回 0007 }
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情”