什麼是雙端隊列?顧名思義就是可以在隊列的兩端進行插入和刪除的隊列。
這樣的雙端隊列既可以當作普通的隊列和棧來使用,還可以用在特定的情況下。
閱讀本文需要先了解雙向鏈表、隊列和棧的知識。
先看一些提前做好的約定
#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;
}