《數據結構》C語言版——循環隊列學習筆記

小萌新今天和大家聊聊什麼是循環隊列,話不多說,開門見山吧!

如果說可以用循環隊列解決隊列的虛假滿的狀態,那麼什麼是虛假滿狀態?

假設當前順序隊列分配的最大空間是6,當隊尾指針從5下標指向6下標時(6下標實際不存在),說明此時隊列已滿,然而依然可以進行出隊的操作,順序隊不能像順序棧那樣進行存儲再分配擴大數組空間,所以隊列的實際可用空間並未佔滿。

循環隊列就是將順序隊列構造成爲一個環狀的隊列空間,如圖:
在這裏插入圖片描述

注意:

此時當指針front=指針rear時,無法判斷隊列空間是滿還是空,有兩種解決思路:
1.另設一個標誌位區分隊列空間是滿還是空;
2.少用一個空間,約定以“隊列頭指針在隊列尾指針的下一位置”作爲隊列滿的狀態標誌。

來吧!舉個栗子吧,如圖:
【情況1】
在這裏插入圖片描述
初始化,隊頭指針front,隊尾指針rear都指向0下標;
當存入數據元素1後,隊尾指針rear從0下標指向1下標;
當存入數據元素2後,隊尾指針rear從1下標指向2下標;
當存入數據元素3後,隊尾指針rear從2下標指向3下標;
當存入數據元素4後,隊尾指針rear從3下標指向4下標;

當存入數據元素8後,隊尾指針rear從7下標指向0下標;(隊列已滿)
如何判斷隊頭指針front與隊尾指針rear重合時,隊列是空還是滿呢?我們假如用flag來標記隊頭指針front與隊尾指針rear的重合狀態,於是規定當flag=0時:隊列爲空;當flag=1時,隊列爲滿。

【情況2】
在這裏插入圖片描述
我們把7下標的空間空出不存放數據,當數據元素7進爲6下標的空間後,隊尾指針rear就指向了爲7下標的空間(此時7下標空間是空的),理論上8個空間只用了7個空間,但在邏輯上我們規定此時隊列已滿。當rear+1後,隊頭指針front與隊尾指針rear重合,說明此時隊列空間邏輯已滿,我們先將隊頭指針所指空間的元素出隊,隊頭指針前移指向1下標,此時再將元素進下標爲7的空間,隊尾指針rear前移(指向0下標),當rear+1後,隊頭指針front與隊尾指針rear又重合,說明此時隊列空間邏輯又已滿,依次循環往復,出隊—>隊頭指針front前移——>後面入隊——>隊尾指針rear前移(判斷條件是rear+1後,隊頭指針front與隊尾指針rear是否重合,不重合則可繼續入隊)。

但是,計算機是沒有環狀的存儲空間的,依然是以線性存儲的方式,那計算機如何判斷什麼時候隊尾指針再次與隊頭指針重合呢?

—————OK!那就是:進行模運算—————

具體模運算是怎麼實現的,文字顯得蒼白無力,還是那句話,把代碼跑起來,慢慢體會!

#include <stdio.h>
#include <malloc.h>
#include <assert.h>

#define MAXSIZE 8//定義隊列初始化存放8個數據元素
typedef int ElmeType;

/*給出順序隊列的結構*/
typedef struct Queue
{
	ElmeType *base;//指針base指向有效的隊列空間
	int      front;//隊頭指針
	int      rear;//隊尾指針指向下一個有效的空間
}Queue;

/*初始化順序隊列*/
void InitQueue(Queue *Q)
{
	Q->base=(ElmeType *)malloc(sizeof(ElmeType)*MAXSIZE);//開闢隊列空間
	assert(Q->base!=NULL);//斷言——是否開闢空間成功
	Q->front=Q->rear=0;//隊頭指針和隊尾指針都指向隊列的0下標,此時隊列爲空
}

/*入隊*/
void EnQueue(Queue *Q,ElmeType x)
{
//數據入隊後,隊尾指針從當前空間指向下一個有效的空間,此時稱隊尾指針是僞指針;
//當僞指針所指下標+1後正好等於隊列空間容量時,此時我們希望僞指針可以重新指向隊頭,而不是出界,於是進行模運算;

//(Q->rear+1)%MAXSIZE——若模爲0,則僞指針恰好指向隊列的最後一個有效空間,我們需要讓此時的僞指針重新指向0下標而不是最後一個有效空間;
//(始終都要將隊列最後一個有效空間空出)循環開始:
	if((Q->rear+1)%MAXSIZE==Q->front)//若僞指針所指下標+1與隊頭指針指向相同的下標,此時判斷爲隊列邏輯已滿
	return;//返回,理論上隊列保留了隊列最後一個有效空間
	Q->base[Q->rear]=x;//否則隊列邏輯不滿,繼續在隊尾指針所指下標進行入隊,入隊完成後,隊尾指針又從當前空間指向下一個有效的空間
	Q->rear=(Q->rear+1)%MAXSIZE;//當邏輯空間滿後,模運算實現隊尾指針重新指向0下標而不是最後一個有效空間;
}

/*展示順序隊列元素*/
void ShowQueue(Queue *Q)
{
	printf("順序隊列中存放的元素:");
	for(int i=Q->front;i!=Q->rear;)
	{
		printf("%d ",Q->base[i]);//依次打印隊頭指針所指下標中的數據到隊尾指針所指下標中的數據
		i=(i+1)%MAXSIZE;//循環打印,7下標不能打印,重新回到0下標(循環時,隊尾下標-隊頭下標=-1)
	}
	printf("\n");
}

/*出隊*/
void DeQueue(Queue *Q)
{
//出隊一個元素,隊頭指針指向下一個有效的數據元素
	if(Q->front==Q->rear)//隊頭隊尾指向相同,隊列爲空
		return;
	Q->front=(Q->front+1)%MAXSIZE;//隊頭指針循環,模運算實現隊頭指針重新指向0下標而不是最後一個有效空間;
}

/*取隊頭元素*/
void GetHead(Queue *Q,ElmeType *v)//指針v帶回隊頭元素
{
//要獲取隊頭,前提是隊列不空
	if(Q->front==Q->rear)//隊列爲空
	return;
	*v=Q->base[Q->front];//必須在base所指的空間裏取元素
}

/*順序隊列的長度*/
int Length(Queue *Q)
{
	return (Q->rear - Q->front);
	//下標0開始存放數據,進隊後,隊尾指針指向下一個有效的新空間
	//隊列中元素的個數正好是隊尾隊頭所指的下標之差
	//但是當隊列邏輯空間滿後,再存儲數據需要先出隊,再進行入隊,此時隊尾隊頭所指的下標之差爲-1
}

/*清除順序隊列*/
void ClearQueue(Queue *Q)
{
	Q->front=Q->rear=0;//隊列置爲空
}

/*銷燬順序隊列*/
void DestroyQueue(Queue *Q)
{
	free(Q->base);//釋放base所指的隊列空間
	Q->base=NULL;//預防野指針
}
/**/

void main()
{
	Queue Q;
	ElmeType e;//定義隊頭元素
	InitQueue(&Q);//&Q是傳入隊列的地址
	printf("將1,2,3,4,5,6,7,8依次入隊\n");
	for(int i=1;i<=8;++i)
	{
		EnQueue(&Q,i);
	}
	ShowQueue(&Q);
	printf("順序隊的長度爲:%d\n",Length(&Q));
	printf("\n");
	printf("進行出隊\n");
	DeQueue(&Q);
	ShowQueue(&Q);
	GetHead(&Q,&e);
	printf("隊頭元素爲:%d\n",e);
	printf("\n");
	printf("將元素10入隊\n");
	EnQueue(&Q,10);
	ShowQueue(&Q);
	printf("\n");
	printf("進行出隊\n");
	DeQueue(&Q);
	ShowQueue(&Q);
	GetHead(&Q,&e);
	printf("隊頭元素爲:%d\n",e);
	printf("\n");
	printf("將元素20入隊\n");
	EnQueue(&Q,20);
	ShowQueue(&Q);
	printf("\n");
	ClearQueue(&Q);
	DestroyQueue(&Q);
}

運行結果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章