队列就是一种FIFO(先进先出)工作的线性表。
在一个分布式系统中,一个队列要为多个队列提供服务。一个具有m个服务器的分布式系统,具有m个服务器队列;一个队列服务于一个服务器。另外,有一个代理人队列。服务请求首先进入代理人队列;代理人按照FIFO方式检查代理人队列中的服务请求,然后把每一个请求送到最对口的服务器队列;服务器按照FIFO方式处理来自服务器队列的服务请求。
之前讲了栈,现在讲队列,其实队列和栈是同一个东西,只不过限制的规则有点不一样,栈是删除和插入都在尾部进行,而队列则是插入在尾部进行,删除则在头部进行。
不过队列却存在一个问题,在头部进行删除的话,那么每次删除都会对所有的元素进行一次移位,导致计算量大大增加,所以和栈一样,队列用两个指示器来对插入和删除操作进行管理,一个指示器指到队列的头部,另一个指向尾部。比如删除操作只是将队列头指示器向后移动一个单位,而不是真正的对数组进行了数据操作,而插入就得是真正的插入了,不过由于删除只是移动指示器,而每移动一次指示器,队列的首尾长度就会变短,所以插入时队尾指示器会将数据放在空着的队头这样就形成了一个循环。
而我们查找元素就得通过一个映射公式进行查找了。
比如:location(i)=(locaiton(队列首元素)+i)%arrayLength
我们查找第五个元素就不能直接用array[5]了,因为array[5]可能存的是其他的元素而要用array[location(5)]进行索引。
当且仅当queueFront=queueBack时,即队首指示与队尾相等时,队列才为空。
不过实际操作用总是将队首指示器指示为第一个元素的起一个位置,书上说这样能简化代码。不过这样有一个问题就是当队列满的时候queueFront=queueBack也成立,所以无法通过这个判断队列是否为空,所以通常的做法是将队列不插满,而保留一个位置。
首先是队列的数组描述。
插入:
template<class T>
void arrayQueue<T>::push(const T& theElement)
{
if((theBack+1)%arrayLength==theFont))//如果theBack+1小于arrayLength则插入到theBack+1位置。
{//如果theBack+1超过arrayLength则存到数组头部,如果数组头部也没位子了,那么只有加长数组长度了。
T *newQueue=new T[2*arrayLength];
int start=(theFront+1)%arrayLength;//因为theFront指向第一个元素的前一个位子,且这个位置不存数据,所以这里要加1.
if(start<2)//没有形成环,即初始位置为1.
copy(queue+start,queue+start+arrayLength-1,newQueue);//直接整个的移动过去,其实start就是1.
else
{
copy(queue+start,queue+arrayLength,newQueue);//将第一段移到队首
copy(queue,queue+theBack+1,newQueue+arrayLength-start)//因为从0开始所以要+1.将第二段移到第一段后面。
}
theFont=2*arrayLength-1;//由于从0开始放数据,所以front在末尾
theBack=arrayLength-2;//本来是arrayLength-1但是front放在了队尾。
arrayLength*=2;
delete [] queue;
queue=newQueue'
}
queueBack=(theBack+1)%arrayLength;
queue[queueBack]=theElement;
}
void pop()
{
if(theFront==theBack)
throw queueEmpty();
theFront=(theFront+1)%arrayLength;
queue[theFront].~T();//书上有这句代码,不过我觉得这句代码没必要,析构掉了,数组就变短了,还怎么形成循环呢?并且push也没有新new内存
}
所以队列的数组实现就是循环,加倍之类的都不够直接,都要通过公式进行额外的处理,稍不注意就容易出错。
下面简单介绍一下队列的链表实现,链表实现的队列就很简单了,和栈的链表实现几乎一样,不过靠两个指针指示当前队首和队尾的位置。
template<class T>
void linkedQueue<T>::push(const T& theElement)
{//把元素theElement插到队尾
//申请新元素
chainNode<T>* newNode=new chainNode<T>(theElement,NULL);
if(queueSize==0)
queueFront=newNode;
else
queueBack->next=newNode;
queueBack=newNode;
queueSize++;
}
template<class T>
void linkedQueue<T>::pop()
{//删除首元素
if(queueFront==NULL)
throw queueEmpty();
chainNode<T>* nextNode=queueFront->next;
delete queueFront;
queueFront=nextNode;
queueSize--;
}