[數據結構 - 第4章] 隊列之順序隊列(C語言實現)


一、什么是順序隊列?

隊列的順序儲存結構:用數組存儲隊列,為了避免當只有一個元素時,隊頭和隊尾重合使得處理變得麻煩,所以引入兩個指針:front 指針指向隊頭元素,rear 指針指向隊尾元素的下一個位置,當 front=rear 時,為空隊列,結構如下圖所示:


順序隊列的結構代碼如下:

typedef int ElemType; /* ElemType類型根據實際情況而定,這里假設為int */

/* 循環隊列的順序存儲結構 */
typedef struct
{
    ElemType data[MAXSIZE];
    int front;      /* 頭指針 */
    int rear;       /* 尾指針,若隊列不空,指向隊列尾元素的下一個位置 */
}SeqQueue;

假設是長度為 5 的數組,初始狀態,空隊列如下圖左所示,front 與 rear 指針均指向下標為 0 的位置。然后入隊 a1、a2、a3、a4,front 指針依然指向下標為 0 位置,而 rear 指針指向下標為 4 的位置,如下圖右所示:


出隊 a1、a2,則 front 指針指向下標為 2 的位置,rear 不變,如下圖左所示,再入隊 a5,此時 front 指針不變,rear 指針移動到數組之外。數組之外, 那將是哪里?如下圖右所示:


問題還不止於此。假設這個隊列的總個數不超過5個,但目前如果接着入隊的話,因數組末尾元素已經占用,再向后加,就會產生數組越界的錯誤,可實際上,我們的隊列在下標為0和1的地方還是空閑的。我們把這種現象叫做 “假溢出"。


二、什么是順序循環隊列?

所以解決 "假溢出" 的辦法就是后面滿了,就再從頭開始,也就是頭尾相接的循環。我們把隊列的這種頭尾相接的順序存儲結構稱為順序循環隊列。

剛才的例子繼續,上圖的 rear 可以改為指向下標為 0 的位置,這樣就不會造成指針指向不明的問題了,如下圖所示:


接着入隊 a6,將它放置於下標為 0 處,rear 指針指向下標為 1 處,如下圖左所示。若再入隊 a7,則 rear 指針就與 front 指針重合,同時指向下標為 2 的位置,如下圖右所示:


此時問題又出來了,我們剛才說,空隊列時,front 等於 rear,現在當隊列滿時,也是 front 等於 rear,那么如何判斷此時的隊列究竟是空還是滿呢?

  • 辦法一是設置一個標志變量 flag, 當 front == rear,且 flag = 0 時為隊列空,當 front== rear,且 flag= 1 時為隊列滿。
  • 辦法二是當隊列空時,條件就是 front = rear,當隊列滿時,我們修改其條件,保留一個元素空間。也就是說,隊列滿時,數組中還有一個空閑單,例如下圖所示,我們就認為此隊列已經滿了,也就是說,我們不允許上圖右的情況出現:


我們重點來討論第二種方法,由於 rear 可能比 front 大,也可能比 front 小,所以盡管它們只相差一個位置時就是滿的情況,但也可能是相差整整一圈。 所以若隊列的最大尺寸為 QueueSize,那么隊列滿的條件是(rear+1) % QueueSize == front ,因此通用的計算隊列長度公式為:(rear - front + QueueSize) % QueueSize

注意:front 指針和 rear 指針后移不能直接使用 ++,而要使用Q->front = (Q->front + 1) % MAXSIZE,因為到達數組尾后需要移動到數組開頭。


三、基本操作

3.1 初始化隊列操作

實現代碼如下:

// 初始化隊列操作
Status initQueue(SeqQueue *Q)
{
	Q->front = 0;
	Q->rear = 0;

	return  TRUE;
}

3.2 入隊操作

實現代碼如下:

// 入隊操作
Status enQueue(SeqQueue *Q, const ElemType e)
{
	// 判斷隊列是否已滿
	if ((Q->rear + 1) % MAXSIZE == Q->front) 
		return FALSE;

	Q->data[Q->rear] = e; // 將元素e賦值給隊尾
	Q->rear = (Q->rear + 1) % MAXSIZE; // rear指針向后移一位置,若到最后則轉到數組頭部

	return  TRUE;
}

3.3 出隊操作

實現代碼如下:

// 出隊操作
Status deQueue(SeqQueue *Q, ElemType *e)
{
	// 判斷是否為空隊
	if (Q->front == Q->rear) 
		return FALSE;

	*e = Q->data[Q->front]; // 將隊頭元素賦值給e
	Q->front = (Q->front + 1) % MAXSIZE; // front指針向后移一位置,若到最后則轉到數組頭部

	return  TRUE;
}

3.3 遍歷隊列操作

實現代碼如下:

// 遍歷隊列操作
Status tarverseQueue(const SeqQueue Q)
{
	int cur = Q.front; // 當前指針
	while (cur != Q.rear) // 直到cur指向了隊尾元素的下一個位置,即Q.rear,結束循環
	{
		printf("%d ", Q.data[cur]);
		cur = (cur + 1) % MAXSIZE; // 當前指針向后推移
	}
	printf("\n");

	return TRUE;
}

四、完整程序

#include <stdio.h>
#include <stdlib.h>  

#define TRUE 1
#define FALSE 0
#define MAXSIZE 5 /* 存儲空間初始分配量 */

typedef int Status;
typedef int ElemType; /* ElemType類型根據實際情況而定,這里假設為int */

/* 順序循環隊列的順序存儲結構 */
typedef struct
{
	ElemType data[MAXSIZE];
	int front; // 頭指針
	int rear; // 尾指針,若隊列不空,指向隊列尾元素的下一個位置
}SeqQueue;

Status initQueue(SeqQueue *Q); // 初始化隊列操作
Status enQueue(SeqQueue *Q, const ElemType e); // 入隊操作
Status deQueue(SeqQueue *Q, ElemType *e); // 出隊操作
Status tarverseQueue(const SeqQueue Q); // 遍歷隊列操作
Status clearQueue(SeqQueue *Q); // 清空隊列操作
Status isEmpty(const SeqQueue Q); // 判斷是否為空隊列
Status getHead(const SeqQueue Q, ElemType *e); // 獲得隊頭元素
int getLength(const SeqQueue Q); // 獲得隊列的長度

// 初始化隊列操作
Status initQueue(SeqQueue *Q)
{
	Q->front = 0;
	Q->rear = 0;

	return  TRUE;
}

// 入隊操作
Status enQueue(SeqQueue *Q, const ElemType e)
{
	// 判斷隊列是否已滿
	if ((Q->rear + 1) % MAXSIZE == Q->front) 
		return FALSE;

	Q->data[Q->rear] = e; // 將元素e賦值給隊尾
	Q->rear = (Q->rear + 1) % MAXSIZE; // rear指針向后移一位置,若到最后則轉到數組頭部

	return  TRUE;
}

// 出隊操作
Status deQueue(SeqQueue *Q, ElemType *e)
{
	// 判斷是否為空隊
	if (Q->front == Q->rear) 
		return FALSE;

	*e = Q->data[Q->front]; // 將隊頭元素賦值給e
	Q->front = (Q->front + 1) % MAXSIZE; // front指針向后移一位置,若到最后則轉到數組頭部

	return  TRUE;
}

// 遍歷隊列操作
Status tarverseQueue(const SeqQueue Q)
{
	int cur = Q.front; // 當前指針
	while (cur != Q.rear) // 直到cur指向了隊尾元素的下一個位置,即Q.rear,結束循環
	{
		printf("%d ", Q.data[cur]);
		cur = (cur + 1) % MAXSIZE; // 當前指針向后推移
	}
	printf("\n");

	return TRUE;
}

// 清空隊列操作
Status clearQueue(SeqQueue *Q)
{
	Q->front = Q->rear = 0;

	return TRUE;
}

// 判斷是否為空隊列
Status isEmpty(const SeqQueue Q)
{
	return Q.front == Q.rear ? TRUE : FALSE;
}

// 獲得隊頭元素
Status getHead(const SeqQueue Q, ElemType *e)
{
	if (Q.front == Q.rear) // 判斷是否為空隊列
		return FALSE;
	*e = Q.data[Q.front];

	return TRUE;
}

// 獲得隊列的長度
int getLength(const SeqQueue Q)
{
	return  (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}

int main()
{
	SeqQueue Q;

	// 初始化隊列
	initQueue(&Q);

	// 入隊操作 
	for (int i = 0; i < MAXSIZE - 1; i++)
		enQueue(&Q, i);
	printf("入隊操作(0、1、2、3)! \n\n");

	// 出隊操作
	ElemType d;
	deQueue(&Q, &d);
	printf("刪除的元素是%d \n\n", d);

	// 遍歷隊列
	printf("遍歷隊列: ");
	tarverseQueue(Q);
	printf("\n");

	// 判斷是否為空隊列
	printf("現在隊列空否? %u (1:空 0:否)\n\n", isEmpty(Q));

	// 獲得隊列的長度
	printf("隊列長度為: %d \n\n", getLength(Q));

	// 獲得隊頭元素
	getHead(Q, &d);
	printf("隊頭元素是%d \n\n", d);

	return 0;
}

輸出結果如下圖所示:


參考:

《大話數據結構 - 第4章》 棧與隊列



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM