數據結構與算法之如何基於順序存儲和鏈式存儲設計一個隊列
前言
上一篇,我們學習了棧的結構,以及基於順序存儲
和鏈式存儲
兩個不同角度如何設計一個棧,以及一些對棧
的常規操作。
那麼棧本篇來看一下隊列
的結構,以及如何基於順序存儲
、鏈式存儲
兩個不同角度設計一個隊列
。
1. 隊列的結構
隊列
是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)
進行刪除操作,而在表的後端(rear)
進行插入操作。
和棧
一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱爲隊尾,進行刪除操作的端稱爲隊頭。
隊列的示意圖:
當隊列
以順序存儲的方式存儲時:
假如長度爲 5 的隊列,初始狀態,空隊列如所示,front
與rear
指針均指向下標爲 0 的位置。然後依次入隊C1
、C2
、C3
,front
指針依然指向下標爲 0 位置,而rear
指針指向下標爲 3 的位置。然後依次C1
、C2
出隊,移動front
到 2 的位置,然後C4
、C5
、C6
入隊,C3
出隊,就會形成下面圖的形式
這時,rear
後面沒有可以入隊的空間,由於隊列
的規則,只能從一段入隊,導致front
前面的位置無法存儲數據,而形成隊列已滿的形式,我們稱這種情況爲 假溢出。
爲了防止 假溢出,我們可以設計一個循環隊列,如下圖:
我們可以犧牲一個存儲單元,來判斷是否滿對。
判斷空對:Q.front = Q.rear
判斷滿對:Q.front = (Q.rear + 1) % MaxSize
2. 順序存儲隊列的設計
2.1 順序存儲隊列的設計
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 // 存儲空間初始化分配量
typedef int Status;
typedef int QElemType;
// 循環隊列的順序存儲結構
typedef struct {
QElemType data[MAXSIZE];
int front; // 頭指針
int rear; // 尾指針,若隊列不空,指向隊列尾元素的下一個位置
}SqQueue;
判斷空對:Q.front = Q.rear
判斷滿對:Q.front = (Q.rear + 1) % MaxSize
2.2 順序存儲隊列的基本操作
- 初始化一個空隊列
// 初始化一個空隊列
Status InitQueue(SqQueue *Q) {
Q->front = 0;
Q->rear = 0;
return OK;
}
- 清空隊列
// 清空隊列
Status ClearQueue(SqQueue *Q) {
Q->front = Q->rear = 0;
return OK;
}
- 判斷隊列是否爲空
Status QueueEmpty(SqQueue Q) {
if (Q.front == Q.rear) { // 空隊列標記
return TRUE;
} else {
return FALSE;
}
}
- 返回元素個數,即:隊列的當前長度
int QueueLength(SqQueue Q) {
// + MAXSIZE,循環隊列,防止出現負數
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
- 返回隊列
Q
的隊頭元素
Status GetHead(SqQueue Q, QElemType *e) {
// 判斷隊列是否爲空
if (Q.front == Q.rear) {
return ERROR;
}
*e = Q.data[(Q.front)];
return OK;
}
- 入隊
Status EnQueue(SqQueue *Q, QElemType e) {
// 判斷是否隊列已滿
if ((Q->rear + 1) % MAXSIZE == Q->front) {
printf("隊列已滿\n");
return ERROR;
}
// 將元素賦值給隊尾
Q->data[Q->rear] = e;
// rear指針後移,若到最後則轉到數組頭部
Q->rear = (Q->rear + 1)%MAXSIZE;
return OK;
}
- 出隊
Status DeQueue(SqQueue *Q, QElemType *e) {
// 判斷是否爲空
if (Q->rear == Q->front) {
return ERROR;
}
// 隊頭元素賦值給e
*e = Q->data[Q->front];
//front 指針向後移動一位,若到最後則轉到數組頭部
Q->front = (Q->front+1)%MAXSIZE;
return OK;
}
- 遍歷隊列元素
Status QueueTraverse(SqQueue Q) {
int i;
i = Q.front;
while (i != Q.rear) {
printf("%d ", Q.data[i]);
i = (i + 1)%MAXSIZE;
}
printf("\n");
// int e;
// for (i = Q.front; i<Q.rear; i++){
// e = Q.data[i];
// printf(" %d ", e);
// }
// printf("\n");
return OK;
}
3. 鏈式存儲隊列的設計
3.1 鏈式存儲隊列的設計
鏈式存儲的隊列,需要設計一個節點
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int QElemType;
// 節點結構
typedef struct QNode {
struct QNode *next;
QElemType data;
}QNode, *QueuePtr;
// 隊列的鏈表結構
typedef struct {
QueuePtr front; // 頭指針
QueuePtr rear; // 尾指針
}LinkQueue;
3.2 鏈式存儲隊列的基本操作
- 初始化隊列
Status InitQueue(LinkQueue *Q) {
// 頭尾指針指向新節點
Q->front = Q->rear = (QueuePtr )malloc(sizeof(QNode));
// 判斷是否創建成功
if (!Q->front) {
return ERROR;
}
// 頭結點的指針域置空
Q->front->next = NULL;
return OK;
}
- 銷燬隊列
Status DestoryQueue(LinkQueue *Q) {
// 遍歷節點,釋放
while (Q->front) {
// 尾指針已經失去意義,作爲臨時遍歷使用
Q->rear = Q->front->next;
free(Q->front);
Q->front = Q->rear;
}
return OK;
}
- 隊列置空
Status ClearQueue(LinkQueue *Q) {
QueuePtr p,q;
// 頭尾指針相等
Q->rear = Q->front;
p = Q->front->next;
Q->front->next = NULL;
while (p) {
q = p;
p = p->next;
free(q);
}
return OK;
}
- 判斷是否爲空,即判斷頭尾節點是否相等
Status QueueEmpty(LinkQueue Q){
if (Q.front == Q.rear)
return TRUE;
else
return FALSE;
}
- 獲取長度
int QueueLength(LinkQueue Q) {
int i = 0;
QueuePtr p = Q.front;
while (p != Q.rear) {
i ++;
p = p->next;
}
return i;
}
也可以在設計隊列
的時候,加一個變量count
,入隊
和出隊
時 +1
或 -1
,來計數。
- 入隊 不用判斷是否滿隊
Status EnQueue(LinkQueue *Q, QElemType e) {
// 創建新節點
QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
if (!s) {
return ERROR;
}
s->data = e;
s->next = NULL;
// 出入隊尾
Q->rear->next = s;
//修改隊尾指針
Q->rear = s;
return OK;
}
- 出隊 需要判斷是否爲空
Status DeQueue(LinkQueue *Q, QElemType *e) {
QueuePtr p;
// 判斷空
if (Q->front == Q->rear) {
return ERROR;
}
p = Q->front->next;
*e= p->data;
Q->front->next = p->next;
//若隊頭就是隊尾,則刪除後將rear指向頭結點.
if(Q->rear == p) Q->rear = Q->front;
free(p);
return OK;
}
- 獲取頭元素
Status GetHead(LinkQueue Q,QElemType *e) {
//隊列非空
if (Q.front != Q.rear) {
//返回隊頭元素的值,隊頭指針不變
*e = Q.front->next->data;
return TRUE;
}
return FALSE;
}
- 遍歷元素
Status QueueTraverse(LinkQueue Q){
QueuePtr p;
p = Q.front->next;
while (p) {
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return OK;
}
···