隊列即先進先出(FIFO)的線性表。怎麼來比喻隊列呢?相當於有一排座位,只能依次從右邊入座,從左邊離開。入座的時候,第一個人坐最左邊,第二個人坐左邊的第二個位置,依次類推;出場的時候,最左邊的人先走。
1. 循環(順序)隊列
普通順序隊列有一個問題,假如中場的時候,最左邊走了幾個人,那麼此時最左邊的人,坐的卻不是最左邊的座位,因爲最左邊的人走後,頭指針就移動到了此時最左邊的人身上(而人無需移動),所以那些空位置就再也用不上了,造成了“假上溢”。所以普通順序隊列沒意思,循環隊列纔可行。
注意:自始至終,這些座位都是編好號的,從左到右依次爲0,1,2,…類似於數組,座位的總個數即MAXSIZE,但是隻能坐MAXSIZE-1個人,隊頭和隊尾之間要留一個空位,即sq->rear+1=MAXSIZE時就意味着坐滿了。
循環隊列的關鍵算法是,判斷隊尾是否還有空座,如果沒有了,就再看看最左邊是否有人離開而騰出來的空座位,如果左邊頭上有人,證明頭尾都坐滿了人,如果左邊有空位,說明已經有人離開了,後來的人就直接坐到左邊去。即:
if( sq->rear+1>= MAXSIZE )
sq->rear=0; //坐不下了,轉到隊頭去,看看隊頭有沒有人。
else
sq->rear++; //坐得下,直接坐到後面
將上面兩句改成如下形式:
sq->rear =(sq->rear+1)%MAXSIZE;
二者等價。實際上,sq->rear+1不會大於MAXSIZE,最多等於MAXSIZE,所以說上面那個if正確的寫法應該是if( sq->rear+1==MAXSIZE ),如果sq->rear+1= MAXSIZE,則sq->rear就等於0,否則sq->rear=餘數= sq->rear+1,所以說if else和求模運算是等價的。
循環隊列的數據結構如下
typedef struct
{
DataType data[QueueSize];
int front;//頭指針
int rear;//尾指針
}CirQueue;
其他操作如下:
// 置隊列空
void Initial(CirQueue *Q)
{
Q->front=Q->rear=QueueSize-1;//注意這個絕對不能寫作-1等,寫成0也可以
}
// 判隊列空
int IsEmpty(CirQueue *Q)
{
return Q->front==Q->rear;
}
//判隊列滿
int IsFull(CirQueue *Q)
{
return (Q->rear+1)%QueueSize==Q->front;//這句很多地方都要用到
}
//入隊
void EnQueue(CirQueue *Q,DataType x)
{
/*關鍵算法:新元素存入隊尾,隊尾再後移一位*/
Q->data[Q->rear]=x; //新元素插入隊尾
Q->rear=(Q->rear+1)%QueueSize; //循環意義下將尾指針加1
}
//出隊
void DeQueue(CirQueue *Q)
{
/*關鍵算法:最左邊的人走後,就將隊頭front往右移一位(即循環意義下+1)*/
Q->front=(Q->front+1)%QueueSize; //循環意義下的頭指針加1
}
2. 鏈隊列
數據結構如下:
typedef struct node{
DataType data;
struct node *next;
}LinkQueue;
typedef struct{
LinkQueue *front; //頭指針
LinkQueue *rear;
}QueueNode;
同樣(見上一篇文章"棧"),關於QueueNode我們也可以用數組LinkQueue *QueueNode[2]來實現。
基本操作
// 置隊列空
void Initial(QueueNode *Q)
{
Q->front=Q->rear=NULL;
}
//判隊列空
int IsEmpty(QueueNode *Q)
{
return Q->front==NULL&&Q->rear==NULL;
}
//進隊列
void Push(QueueNode *Q,DataType x)
{//將元素x插入鏈隊列尾部
LinkQueue *p=(LinkQueue *)malloc(sizeof(LinkQueue));//申請新結點
p->data=x;
p->next=NULL;
if(IsEmpty(Q))
Q->front=Q->rear=p; //將x插入空隊列
else
{
Q->rear->next=p; //(Q->rear)是隊尾的指針,即:使隊尾結構體的next指針指向p
Q->rear=p; //隊尾指針指向新的尾
}
}
//出隊列
DataType Pop(QueueNode *Q)
{
LinkQueue *p;
p=Q->front; //保存隊頭結點
Q->front=p->next; //使隊頭指向p後面的一位
if(Q->rear==p)//原隊中只有一個結點的情況,刪去後隊列變空,所以尾指針也應置空
Q->rear=NULL;
free(p); //釋放被刪隊頭結點
}
注意上面列出的只是算法的關鍵部分,並不完整,刪減了一些無礙算法邏輯的內容。