1. 程式人生 > >佇列的鏈式儲存結構及實現

佇列的鏈式儲存結構及實現

佇列的鏈式儲存結構,其實就是線性表的單鏈表,只不過它只是尾進頭出而已,我們把它簡稱為鏈佇列為了操作上的方便,我們將隊頭指標指向鏈佇列的頭結點,而隊尾指標指向終端節點。如果


空佇列時,front和rear都指向頭結點。



入隊操作:

在隊尾新增元素,先將隊尾元素的next指向新增的元素,然後將隊尾指標重新指向新的隊尾即可。


出隊操作:

頭結結點指向的結點即為隊頭結點,出隊操作,就是把隊頭結點幹掉,先把頭結點指向新的隊頭結點(也就是舊的隊頭結點的後繼結點),然後釋放舊的隊頭結點。如果連結串列除頭結點外只剩一個元素時,則需將rear指向頭結點即可


下面是佇列鏈式儲存結構實現的具體程式碼:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#define QUEUESIZE 10
#define ERROR 0 
#define OK 1 
#define TRUE 1 
#define FALSE 0
#define EleType int 
#define Status int
//鏈佇列結點
typedef struct QueueNode
{
	EleType e;//資料域
	struct QueueNode* next;//指標域
}QueueNode,*LinkQueuePoi;
typedef struct LinkQueue
{
	LinkQueuePoi front;//指向頭結點
	LinkQueuePoi rear;//指向隊尾
}LinkQueue;
/*
初始化鏈佇列
鏈佇列為空時,鏈佇列隊頭指標隊尾指標均指向頭結點
*/
Status InitLinkQueue(LinkQueue* queue)
{
	//空指標
	if (!queue)
	{
		return ERROR;
	}
	QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));//頭結點
	node->next = NULL;
	queue->front = queue->rear = node;
	return OK;
}
/*
清空鏈佇列
將所有元素釋放
*/
Status CleaerLinkQueue(LinkQueue* queue)
{
	//空指標
	if (!queue)
	{
		return ERROR;
	}
	//空鏈佇列
	if (queue->front == queue->rear)
	{
		return ERROR;
	}
	QueueNode* node = queue->front->next;//隊頭元素
	while (node)
	{

		queue->front->next = node->next;//指向新的隊頭結點
		if (queue->rear == node)//當刪除的是隊尾元素時,將隊尾指標指向頭結點
		{
			queue->rear = queue->front;
		}
		free(node);//釋放舊的隊頭結點
		node = queue->front->next;
	}
	return OK;
}
/*
判斷鏈佇列是否為空佇列
*/
Status EmptyLinkQueue(LinkQueue* queue)
{
	//空指標
	if (!queue)
	{
		return ERROR;
	}
	//空鏈佇列
	if (queue->front == queue->rear)
	{
		return TRUE;
	}
	return FALSE;
}
/*
獲取鏈佇列長度
*/
int LengthLinkQueue(LinkQueue* queue)
{
	//空指標
	if (!queue)
	{
		return ERROR;
	}
	//空鏈佇列
	if (queue->front == queue->rear)
	{
		return 0;
	}
	QueueNode* node = queue->front->next;
	int num = 0;
	while (node)
	{
		node = node->next;
		num++;
	}
	return num;
}
/*
在鏈佇列隊尾新增元素
先將新元素新增到連結串列尾部,然後將佇列尾指標指向這個新元素
*/
Status AddQueue(LinkQueue* queue,EleType e)
{
	//空指標
	if (!queue)
	{
		return ERROR;
	}
	QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));
	if (!node)
	{
		return ERROR;
	}
	node->next = NULL;
	node->e = e;
	queue->rear->next = node;//將新結點新增到連結串列表中
	queue->rear = node;//隊尾指標指向新的隊尾結點
	return OK;
}
/*
從鏈佇列中刪除隊頭元素
先將頭結結點指向新的隊頭結點,然後釋放原來的隊頭結點
*/
Status DelQueue(LinkQueue* queue, EleType *e)
{
	//空指標
	if (!queue)
	{
		return ERROR;
	}
	//注意queue->front是頭結點,頭結點指向的結點才是隊頭結點
	QueueNode* node = queue->front->next;//舊隊頭結點
	*e = node->e;
	queue->front->next = node->next;//隊頭指標指向新的隊頭結點
	//當刪除的是隊尾元素時,將隊尾指標指向頭結點
	if (node = queue->rear)
	{
		queue->rear = queue->front;
	}
	return OK;
}
/*
列印鏈佇列元素
*/
void PrintfLinkQueue(LinkQueue* queue)
{
	if (!queue)
	{
		return;
	}
	QueueNode* node = queue->front->next;
	while (node)
	{
		printf("%d,", node->e);
		node = node->next;
	}
	printf("\n");
	return;
}
int main(int argc, char *argv[])
{
	LinkQueue queue;
	InitLinkQueue(&queue);
	AddQueue(&queue, 1);
	AddQueue(&queue, 2);
	AddQueue(&queue, 3);
	AddQueue(&queue, 4);
	AddQueue(&queue, 5);
	AddQueue(&queue, 6);
	AddQueue(&queue, 7);
	AddQueue(&queue, 8);
	AddQueue(&queue, 9);
	printf("鏈佇列元素個數:%d\n",LengthLinkQueue(&queue));
	printf("展示元素:\n");
	PrintfLinkQueue(&queue);
	int e1, e2;
	DelQueue(&queue, &e1);
	DelQueue(&queue, &e2);
	printf("刪除元素:%d,%d\n", e1, e2);
	printf("展示元素:\n");
	PrintfLinkQueue(&queue);
	printf("鏈佇列元素個數:%d\n", LengthLinkQueue(&queue));
	CleaerLinkQueue(&queue);
	printf("清空元素後,長度為%d,rear = %p,front=%p",LengthLinkQueue(&queue), queue.rear,queue.front);
	printf("\n");
	return 0;
}
驗證結果截圖:

對於迴圈佇列與鏈佇列的比較,可以從時間和空間2方面來考慮,從時間上,他們的基本操作都是常數時間,即都為O(1),不過迴圈佇列是事先申請好空間,使用期間不釋放,而對於鏈佇列,每次申請和釋放結點也會存在一些時間開銷,如果入隊出隊頻繁,則2者還是有些細微的差異。對於空間方面來說,迴圈佇列必須有一個固定的長度,所以就有了儲存元素個數和空間浪費的問題。而鏈佇列就不存在這個問題,儘管它需要一些指標域,會產生一些空間上的開銷,但也可以接受。所以在空間上,鏈佇列更加靈活。