數據結構:基於鏈式存儲的雙端隊列

什麼是雙端隊列?顧名思義就是可以在隊列的兩端進行插入和刪除的隊列。

這樣的雙端隊列既可以當作普通的隊列和棧來使用,還可以用在特定的情況下。

閱讀本文需要先了解雙向鏈表、隊列和棧的知識。

先看一些提前做好的約定

#define ElemType int

typedef struct LinkNode{ // 每個結點雙向鏈表類型的
	ElemType data;
	struct LinkNode *prior; // 前向指針 
	struct LinkNode *next; // 後向指針 
}LinkNode;

typedef struct{ // 雙端隊列
	LinkNode *front; // 隊首指針 指向第一個隊列的元素 
	LinkNode *rear; // 隊尾指針 指向隊列的最後一個元素 
}LinkDeque;

首先要了解一下如何初始化和判斷隊列爲空,如何判斷隊列爲空往往是由初始化操作而定

// 初始化 
void DequeInit(LinkDeque &dq)
{
	dq.front = dq.rear = NULL;
}
// 隊列判空函數 
bool DequeIsEmpty(LinkDeque dq)
{
	return (dq.front == NULL) ? true : false;
}

既然是雙端隊列,那麼肯定可以從兩端都可以入隊,那麼怎麼實現呢?其實我們在調用庫函數的時候會發現有些函數會有一個true,false的參數選項,可填可不填用來區分兩種情況。我們也借鑑這種方法。

// 入隊,默認從隊尾入隊 
bool DequePush(LinkDeque &dq, ElemType e, bool flag = true)
{
	LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
	if(newNode == NULL){ // 沒有申請到空間 
		return false;
	}
	newNode->data = e;
	if(DequeIsEmpty(dq)){ // 如果隊列爲空 
		newNode->prior = NULL;
		newNode->next = NULL;
		dq.front = newNode;
		dq.rear = newNode;
	}
	else if(flag == true){ // flag==true 從隊尾插入 
		newNode->next = NULL; // 新插入的結點後面沒有結點 
		newNode->prior = dq.rear;
		dq.rear->next = newNode; // 先連接新的結點 
		dq.rear = newNode; // 後修改隊尾指針 
	} 
	else{ // 從隊首插入 
		newNode->next = dq.front;
		newNode->prior = NULL; // 新插入的結點後面沒有結點 
		dq.front->prior = newNode; 
		dq.front = newNode; 
	} 
	return true; 
}

出隊也是同理,但是出隊的時候隊列爲空和隊列中只有一個元素要做特殊處理。隊列爲空特判好理解,爲什麼只有一個元素也要特判呢?其實問題就出在刪除操作上
LinkNode *tmp = dq.front;
dq.front = dq.front->next;  -->這裏如果只有一個元素的話,會使dq.front變成空指針
dq.front->prior = NULL;    -->進而造成在這一步dq.front->prior非法訪問
free(tmp); 
 

// 出隊,默認從隊首出隊 
bool DequePop(LinkDeque &dq, bool flag = true) 
{
	if(DequeIsEmpty(dq)){ // 如果隊列爲空 
		return false;
	} 
	else if(dq.front == dq.rear){ // 如果隊列中只剩一個結點 
		LinkNode *tmp = dq.front;
		dq.front = dq.rear = NULL;
		free(tmp); 
	} 
	else if(flag == true){ // 從隊首出隊 
		LinkNode *tmp = dq.front;
		dq.front = dq.front->next;
		dq.front->prior = NULL;
		free(tmp); 
	}
	else{ // 從隊尾出隊 
		LinkNode *tmp = dq.rear;
		dq.rear = dq.rear->prior;
		dq.rear->next = NULL;
		free(tmp);
	}
	return true;
}

完整代碼:

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

#define ElemType int

typedef struct LinkNode{ // 每個結點雙向鏈表類型的
	ElemType data;
	struct LinkNode *prior; // 前向指針 
	struct LinkNode *next; // 後向指針 
}LinkNode;

typedef struct{
	LinkNode *front; // 隊首指針 指向第一個隊列的元素 
	LinkNode *rear; // 隊尾指針 指向隊列的最後一個元素 
}LinkDeque;

// 函數前置聲明 
bool DequeIsEmpty(LinkDeque dq); 
// 初始化 
void DequeInit(LinkDeque &dq)
{
	dq.front = dq.rear = NULL;
}

// 入隊,默認從隊尾入隊 
bool DequePush(LinkDeque &dq, ElemType e, bool flag = true)
{
	LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
	if(newNode == NULL){ // 沒有申請到空間 
		return false;
	}
	newNode->data = e;
	if(DequeIsEmpty(dq)){ // 如果隊列爲空 
		newNode->prior = NULL;
		newNode->next = NULL;
		dq.front = newNode;
		dq.rear = newNode;
	}
	else if(flag == true){ // flag==true 從隊尾插入 
		newNode->next = NULL; // 新插入的結點後面沒有結點 
		newNode->prior = dq.rear;
		dq.rear->next = newNode; // 先連接新的結點 
		dq.rear = newNode; // 後修改隊尾指針 
	} 
	else{ // 從隊首插入 
		newNode->next = dq.front;
		newNode->prior = NULL; // 新插入的結點後面沒有結點 
		dq.front->prior = newNode; 
		dq.front = newNode; 
	} 
	return true; 
}

// 出隊,默認從隊首出隊 
bool DequePop(LinkDeque &dq, bool flag = true) 
{
	if(DequeIsEmpty(dq)){ // 如果隊列爲空 
		return false;
	} 
	else if(dq.front == dq.rear){ // 如果隊列中只剩一個結點 
		LinkNode *tmp = dq.front;
		dq.front = dq.rear = NULL;
		free(tmp); 
	} 
	else if(flag == true){ // 從隊首出隊 
		LinkNode *tmp = dq.front;
		dq.front = dq.front->next;
		dq.front->prior = NULL;
		free(tmp); 
	}
	else{ // 從隊尾出隊 
		LinkNode *tmp = dq.rear;
		dq.rear = dq.rear->prior;
		dq.rear->next = NULL;
		free(tmp);
	}
	return true;
}

// 默認獲取隊首元素 
ElemType GetElem(LinkDeque dq, bool flag = true)
{
	if(DequeIsEmpty(dq)){
		printf("雙端隊列爲空");
		exit(1);
	}
	return (flag == true) ? dq.front->data : dq.rear->data;
}

// 計算隊列中的元素個數 
int DequeSize(LinkDeque dq)
{
	int length = 0;
	LinkNode *p = dq.front;
	while(p != NULL){
		length++;
		p = p->next;
	}
	return length;
}

// 隊列判空函數 
bool DequeIsEmpty(LinkDeque dq)
{
	return (dq.front == NULL) ? true : false;
}


int main() 
{
	LinkDeque dq;
	DequeInit(dq);
	for(int i = 0; i < 10; i++){
		DequePush(dq, i+1);
//		printf("%d\n", GetElem(dq));
	}
	printf("length = %d\n", DequeSize(dq)); 
	for(int i = 0; i < 10; i++){
		printf("%d\n", GetElem(dq));
		DequePop(dq);
		printf("length = %d\n", DequeSize(dq));
	}
	
	
	return 0;
}

 

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