队列是一种先进后出的数据结构(见书)。元素从尾部进入队列,然后从头部离开。因此,队列是
一种等候列表,类似于我们在银行或者商店等候服务或者缴款时所排的队伍,人则像对列中的元素
,按照他们到达队伍的顺序接受服务。
4.2.1 队列的使用
在多任务操作系统中,对列被用来保存等待CPU访问的进程,最常见的一种用法被称为round-
robin调度,它经常用在诸如LINUX这样的操作系统中。
新近创建的进程进入队列的尾部。在CPU可用时,操作系统将进程发送到队列的头部并执行进程
。进程会一直进行,直到出现下列情形之一:分配给它的CPU时间(quantum)用完;等待I/O操作,
或者发生一个中断。
如果进程由于分配给它的时间用完或者发生中断而交出CPU的控制权,它会重新进入队列末尾,
但是如果是由于等待I/O操作而停止执行,那么它会被阻塞,在I/O操作完成之后,它才能重新进入
队列,只有那些准备就绪听候CPU访问的进程才能进入队列。
使用队列调度进程对于每个进程的CPU时间分配来说是一种相对公平的做法,短进程无需因为等
待长进程而受到不公平的待遇。最终的吞吐量和最终用户相应时间都是可以接受的。
虽然如此,较长的进程需要多次通过队列才能完成作业。某些操作系统试图通过为程序赋予优先
级来解决长进程遭受到的不公平待遇,进程在队列中等待的时间越长,它的优先级就越高。操作系
统会首先发送拥有优先级的进程。在这种情况下,操作系统需要直接访问对列中的元素。
注意:为所有长度的进程提供一个绝对公平的进程调度算法,或者在所有用户得到相同相应时间
的同时提高吞吐量是一件不可能的事情。在设计进程调度方案时,必须综合多种因素的影响。
4.2.2 使用数组实现队列
和堆栈一样,队列的实现既可以使用数组也可以使用链表。采用数组的实现方式体现了操作系
统由主内存维护进程队列的操作方式,而且能够直接访问对列中的各个元素,但是由于数组的大小
在分配之时就已经确定,所以在可供使用的空间上存在限制。
队列的节点类和堆栈的节点类完全相同(见程序4-1和程序4-2)。队列也通过元素在暑假数据
结构中的位置来访问元素,而不是使用关键字,因此,不需要编写一个Mix-In类来强制实现getKey
函数。
注意:同堆栈一样,队列不需要使用节点,因为队列的成员关系不要求指向成员对象的指针。所以
,队列可以将指向对象的指针保存在队列中,我们在这些列子中使用节点是为了与本书的其他例子
保持一致。
队列类(见程序4-12和程序4-13)包含这样一些函数:可以向对列中添加元素(元素的入列),从
对列中删除元素(出列),根据元素在对列中的位置取得元素,确定对列是否为空,以及取得队列
元素的当前数量。
[程序4-12]使用数组时队列类的声明
#ifndef QUEUEMGR
#define QUEUEMGR
#include "Thing.h"
#include "node.h"
#define MAX_ELEMENTS 10
class QueueMgr
{
private:
int queue_end;
Node*theQueue(MAX_ELEMENTS);
public:
QueueMgr();
bool enqueue(Thing*);//pass in a single element
//pass in ordinal position of element
bool getElement(int,Thing*&);
bool dequeue(Thing*&);
bool is_empty();//true if queue is empty
int getSize();//return total elements in the array
};
#endif
[程序4-13]使用数组时对列类的具体实现
#include "QueueMgr.h"
QueueMgr::QueueMgr()
{
queue_end=o;
}
bool QueueMgr::enqueue(Thing*newObject)
{
bool result;
Node*theNode=new Node(newObject);
if (queue_end<MAX_ELEMENTS)
{
theQueue[queue_end]=theNode;
queue_end++;
result=true;
}
else
result=false;
return result;
}
bool QueueMgr::getElement(int position,Thing*&element)
{
int result=true;
if(position>queue_end||queue_end==0)
result=false;
else
{
Node*theNode=theQueue[position];
element=theNode->getThing();
}
return result;
}
bool QueueMgr::dequeue(Thing*&element)
{
bool result =true;
if(queue_end==0)
result=false;
else
{
Node*theNode=theQueue[0];
element=theNode->getThing();
for(int i=0;i<queue_end-1;i++)
theQueue[i]=theQueue[i+1];
queue_end--;
}
return result;
}
bool QueueMgr::is_empty()
{
return (queue_end==0);
}
int QueueMgr::getSize90
{
return queue_end==0;
}
int QueueMgr::getSize()
{ return queue_end;)
从理论上说,队列管理器类应该有两个指针,一个指向队列的头部,一个指向队列的尾部。但是,
在使用数组实现队列的时候,如果能够保证对列的第一个元素总是保存在数组元素0中,那么只需
要对队列的尾部保持跟踪即可。
为了将一个元素放入队列,队列管理器需要执行以下操作:
(1)创建一个新的节点,以便将对象链接到队列
(2)如果对列中有空间可用,将新节点插入到队列的尾部,并增加指向对列尾部的指针,返回
true
(3)否则,返回fasle
将一个元素出列要稍微复杂一些,因为需要将数组中的所有元素向前移动一个位置,以保证队列的
第一个元素始终保存在数组元素0中。
(1)确定队列是否为空,如果为空,则返回false并退出函数
(2)否则,取得对列顶部的节点
(3)取得队列顶部的节点所指向的对象
(4)删除节点对象
(5)执行下一个循环,从数组元素1开始,将所有后续元素向前移动一个为位置,
(6)减小指向对列尾部的指针
(7)返回true
4.2.3 使用链表实现对列
在采用链表实现队列时,队列类看上去就和期望中的更加相似了。它有头和尾两个指针,提供
了对队列两端的访问(见程序4-14和程序4-15)。该队列类使用的节点类与我们前面实现堆栈时所
使用的节点类完全相同(见程序4-8和程序4-9)。
【程序4-14】使用链表时队列类的声明
#ifndef QUEUE
#define QUEUE
#include "Thing.h"
#include "node.h"
class Queue
{
private:
Node*head,*tail;
public:
Queue();
int is_empty();
bool enqueue (Thing*)
Thing*dequeue();
Node*getHead();
};
#endif
[程序4-15]使用链表事队列类的具体实现。
#include "queque.h"
Queue::Queue()
{
head=0;
}
int Queue::is_empty()
{return (head==0);}
bool Queue ::enqueue (Thing*theThing)
{
Node*theNode=new Node(theThing):
if(!theNode)
{
cout<<"/nCannot creat node."
return;
}
if(is_empty)
{
head=theNode;
tail=theNode;
}
else
{
tail->setNext(theNode);
tail=theNode;
}
return true;
Thing*Queue::dequeue()
{
Node*theNode=head;
head=theNode->getNext90;
Thing*theThing=theNode->getThing90;
delete theNode;
return theThing;
}
Node*Queue::getHead()
{
Node*theNode=head;
return theNode;
}
为了将一个对象放进队列,使用链表实现的队列类需要完成如下操作:
(1)创建一个节点,将对象链接到队列
(2)如果队列为空,则将队列的头指针和尾指针均指向新节点,然后跳到步骤5.
(3)否则,将尾节点的next指针指向新节点
(4)设置队列管理器的tail变量,使其指向新节点
(5)返回true
注意:对链表的插入操作应该不会失败,除非程序用尽了内存,通过检查新节点是否返回成功关键
,可以得知是否发生了错误。
在使用链表实现队列时,将对象从队列冲删除的过程比使用数据时要简单一些
(1)取得位于队列头部的节点
(2)设置队列管理器的head变量,使其指向队列当前就节点的后继节点。
(3)取得队列当前的头节点所指向的对象
(4)删除节点
(5)返回队列节点所指向的对象。
4.3 小结
堆栈是一种后入先出式的数据结构,可以使用数组或链表实现。在程序执行期间,操作系统一
般使用堆栈保存函数返回地址和传递参数。其他数据结构(例如二叉树)也使用堆栈使用遍历操作
。
队列是一种先入后出式的数据结构,也可以使用数组或链表实现。例如,在操作系统可以使用队
列保存等待CPU访问的进程
***********************以上资料来源于《面向对象c++数据结构》--【美】Jan Harrington
陈博译
***********************************************************************************