1. 程式人生 > >資料結構學習(六)——迴圈順序佇列的操作

資料結構學習(六)——迴圈順序佇列的操作

佇列是一種只允許在表的一端(稱為隊尾)進行插入,而在另一端(稱為隊頭)進行刪除的線性表,是線性表的一種特例。

順序佇列是用陣列結構來表示的。為了描述佇列的這種結構,我們需要兩個表明隊頭和隊尾的指標,規定隊頭指標指向佇列頭結點的前一個位置,而隊尾指標指向佇列的尾結點。而為什麼又會用到迴圈佇列呢,因為為了防止“假上溢”對空間造成的浪費。“假上溢”是這麼來的,先說“上溢”和“下溢”。下溢就是當空對時,如果再做出隊操作,則會產生“下溢”。

當隊滿時,再做入對操作會產生“上溢”。但是,如果當前尾指標等於陣列的上界,即使佇列不滿,再做入隊操作也會引起溢位。這種現象就是“假上溢”。然而有了迴圈佇列,我們就可以避免假上溢的問題了。但是迴圈佇列還是有一個問題,就是判斷佇列與隊滿的情況。如果按照前面的規定,隊頭指標指向佇列頭結點的前一個位置,而隊尾指標指向佇列的尾結點。那麼是分辨不出來隊滿與隊空的關係的。我看書中列出了3種解決此問題的方法。不過第三種最適合,它是採用少用一個節點空間,即頭指標指向的空間不使用。這樣的話。當front等於rear時,隊空;入隊後,當尾指標加1後等於頭指標,即:(rear+1)%maxsize = front.則說明這時佇列已滿。

下面是對迴圈順序佇列操作的一個整體程式碼:

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

#define MAXSIZE 100

typedef struct node
{
	int data[MAXSIZE];
	int front;			//存放佇列頭地址
	int rear;			//存放佇列尾地址
}sequeue;

void Sequeue_SetNull(sequeue *q);		//佇列置空
int Sequeue_Empty(sequeue *q);			//判斷佇列是否為空
int Sequeue_GetFront(sequeue *q);		//得到佇列頭結點
int Sequeue_In(sequeue *q, int dat);	//入對操作
int Sequeue_Out(sequeue *q);			//出隊操作
void ShowSequeue(sequeue *q);			//輸出顯示佇列

int main(void)
{
	int num, i, dat, choice, ans;
	sequeue *q;

	q = (sequeue*)malloc(sizeof(sequeue));

	printf("迴圈順序佇列的操作練習:\n");
	printf("輸入你想建立佇列的資料個數:\n");
	scanf("%d", &num);
	if(num>MAXSIZE-1)
	{
		printf("佇列結點數大於最大允許空間!\n");
		return 0;
	}
	Sequeue_SetNull(q);	
	printf("請依次輸入佇列資料:\n");
	for(i=1; i<num+1; i++)			//迴圈輸入佇列結點資料
	{
		scanf("%d", &dat);
		q->data[i] = dat;
		q->rear = i;
	}
	while(1)
	{
		printf("佇列操作:\n");
		printf("1.取佇列結點\n");
		printf("2.入隊操作\n");
		printf("3.出隊操作\n");
		printf("4.輸出佇列\n");
		printf("5.退出程式\n");
		printf("做出選擇:\n");
		scanf("%d", &choice);

		switch(choice)
		{
		//取佇列頭結點
		case 1:
			dat = Sequeue_GetFront(q);
			printf("頭結點為:%d\n", dat);
			break;
		//入隊
		case 2:
			printf("輸入你想入隊的資料:\n");
			scanf("%d", &dat);
			ans = Sequeue_In(q, dat);
			if(ans)
				printf("入隊成功!\n");
			else
				printf("入隊失敗!\n");
			break;
		//出隊
		case 3:
			ans = Sequeue_Out(q);
			if(ans)
				printf("出隊成功!\n");
			else
				printf("出隊失敗!\n");
			break;
		//輸出顯示佇列
		case 4:
			ShowSequeue(q);
			break;
		//退出程式
		case 5:
			return 0;
			break;
		default:
			printf("選擇無效!\n");
			break;
		}
	}
}	

//佇列置空
void Sequeue_SetNull(sequeue *q)
{
	q->rear = 0;				//其中陣列第一個節點空間不使用(即下標為0陣列元素),用作佇列滿與空的區別
	q->front = 0;					
}

//判斷佇列是否為空
int Sequeue_Empty(sequeue *q)
{
	if(q->front == q->rear)		//頭結點等於尾結點即表示佇列為空
		return 1;
	else
		return 0;
}

//取得佇列頭結點
int Sequeue_GetFront(sequeue *q)
{
	if(Sequeue_Empty(q))		//取頭結點先要判斷佇列是否為空
	{
		printf("佇列空!\n");
		return 0;
	}
	else
		return q->data[(q->front+1) % MAXSIZE];
}

//入隊
int Sequeue_In(sequeue *q, int dat)
{
	if((q->rear+1)%MAXSIZE == q->front)				//入隊先要判斷佇列是否已經滿了
		return 0;
	else
	{
		q->rear = (q->rear+1) % MAXSIZE;			//使用求模運算
		q->data[q->rear]= dat;
		return 1;
	}
}

//出隊
int Sequeue_Out(sequeue *q)
{
	if(Sequeue_Empty(q))				//出隊先要判斷佇列是否為空
		return 0;
	else
	{
		q->front = (q->front+1) % MAXSIZE;		//求模運算
		return q->data[q->front];
	}
}

//輸出顯示佇列
void ShowSequeue(sequeue *q)
{
	int i;

	i = q->front+1;
	while(i != q->rear+1)
	{
		printf("%d ", q->data[i]);
		i++;
	}
	printf("\n");
}