【c 語言資料結構】棧和佇列的相關操作
棧
只允許在一端進行插入或刪除操作的線性表。
一,棧的基本操作介紹
- InitStack(&S):初始化棧。構造一個空棧S,分配記憶體空間。
- DestroyStack(&L):銷燬棧。銷燬並釋放棧S 所佔用的記憶體空間。
- Push(&S,x):進棧,若棧S 未滿,則將x 加入使之稱為新棧頂。
- Pop(&S,&x):出棧,若棧非空,則彈出棧頂元素,並用x 返回。
- GetTop(S,&x):讀棧頂元素,若棧S 非空,則用x 返回棧頂元素。
二,棧的順序儲存實現
1,順序棧的定義
見如下程式碼片段,體會順序棧的定義:
#define MaxSize 10
typedef struct{
ElemType data[MaxSize]; //靜態陣列存放棧中元素
int top; //棧頂指標
} SqStack;
void testStack(){
SqStack S; //宣告一個順序棧(分配空間)
//.....
}
2,初始化操作
//初始化棧 void InitStack(SqStack &S){ S.top=-1; //初始化棧頂指標 } //我們也可以知道如何判斷棧空 bool StackEmpty(SqStack S){ if(S.top==-1) //棧空 return true; else return false; }
3,進棧操作
//新元素入棧
bool Push(SqStack &S,ElemType x){
if(S.top==MaxSize-1) //棧滿,報錯
return false;
S.data[++S.top]=x; //指標先加1,新元素入棧
return true;
}
4,出棧操作
在棧頂彈出一個元素,並用x 返回:
bool Pop(SqStack &S,ElemType &x){ if(S.top==-1) //棧空,報錯 return false; x=S.data[S.top]; //棧頂元素先出棧(注意一下和指標變化的先後順序) S.top=S.top-1; //指標再減1 return true; }
三,棧的鏈式儲存實現
採用鏈式儲存的棧稱為鏈棧,其優點是便於多個棧共享儲存空間和提高其效率,且不存在棧滿上溢的情況。通常採用單鏈表實現,並規定所有的操作都是在單鏈表的表頭進行的。
現在再想一想,是不是在單鏈表中,表頭後插入一個結點就相當於元素入棧的操作,在表頭後刪除一個元素就相當於元素出棧。只不過入棧和出棧的操作都是在連結串列的表頭進行的。
佇列
一,佇列的定義
前面我們知道了棧是隻允許在一端進行插入或刪除操作的線性表,那佇列呢?
佇列是隻允許在一端進行插入,在另一端刪除的線性表。這個結合日常生活中的排隊來想,是不是非常簡單~
以下是佇列的幾種基本操作:
- InitQueue(&Q):初始化佇列,構造一個空佇列Q。
- DestroyQueue(&Q):銷燬佇列。銷燬並釋放佇列Q 所佔用的記憶體空間
- EnQueue(&Q,x):入隊,若佇列Q 未滿,將x 加入,使之成為新的隊尾。
- DeQueue(&Q,&x):出隊,若佇列Q 非空,刪除隊頭元素,並用x 返回。
二,佇列的順序實現
1,初始化操作
#define MaxSize 10 //定義佇列中元素的最大個數
typedef struct {
ElemType data[MaxSize]; //用靜態陣列存放佇列元素
int front,rear; //隊頭指標和隊尾指標
}SqQueue;
//初始化佇列
void InitQueue(SqQueue &Q){
//初始時,隊頭隊尾指標指向0
Q.rear=Q.front=0;
}
//判斷佇列是否為空,就是頭,尾指標指向同一個元素
bool QueueEmpty(SqQueue Q){
if(Q.rear==Q.front) //隊空的條件
return true;
else
return false;
}
2,入隊操作
//入隊
bool EnQueue(SqQueue &Q,ElemType x){
if(佇列已滿)
return false; //隊滿則報錯
Q.data[Q.rear]=x; //新元素插入隊尾
Q.rear=(Q.rear+1)%MaxSzie //隊尾指標加1取模
}
上述程式中的判斷佇列是否已滿的操作我們後面再說,這裡的模運算操作其實就是將無限的整數域對映到有限的整數集合{0,1,2,.....,MaxSize-1} 上,將儲存空間在邏輯上變成了“環狀”。
結合迴圈佇列,我們可以知道,佇列已滿的條件:隊尾指標的再下一個位置是對頭,即 (Q.rear+1)%MaxSize==Q.front)
3,出隊操作
只能讓隊頭元素出隊。刪除一個隊頭元素,並用x 返回
bool DeQueue(SqQueue &Q,ElemType &x){
if(Q.rear==Q.front) //判斷隊空
return false;
x=Q.data[Q.front];
Q.front=(Q.front+1)%MaxSize; //隊頭指標後移
return true;
}
那如何知道佇列元素的個數呢?這裡有一個公式,希望我們記住:(rear+MaxSize-front)%MaxSize ,自己可以驗證一下這個公式的合法性。
三,佇列的鏈式實現
1,佇列的鏈式實現
程式碼如下:
typedef struct LinkNode{ //鏈式佇列結點
Elemtype data;
struct LinkNode *next;
}LinkNode;
typedef struct{ //鏈式佇列
LinkNode *front,*rear; //佇列的隊頭和隊尾指標
}LinkQueue;
初始化佇列(帶頭結點)的實現:
void InitQueue(LinkQueue &Q){
//初始時 front、rear都指向頭結點
Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode));
Q.front->next=NULL;
}
void testLinkQueue(){
LinkQueue Q; //宣告一個佇列
InitQueue(Q); //初始化佇列
//... 後續操作...
}
示意圖如下:
由此可知,判斷一個佇列是否為空的程式碼實現如下:
bool IsEmpty(LinkQueue Q){
if(Q.front==Q.rear)
return true;
else
return false;
}
不帶頭結點的佇列初始化:
void InitQueue(LinkQueue &Q){
//初始時,front、rear 都指向NULL
Q.front=NULL;
Q.rear=NULL;
}
//此時判斷佇列是否為空即隊頭指標為NULL
bool IsEmpty(LinkQueue Q){
if(Q.front==NULL)
return true;
else
return false;
}
2,入隊
帶頭結點 的新元素入隊操作只需要記得只能從隊尾新增元素即可,邏輯也比較簡單,直接見如下程式碼片段:
void EnQueue(LinkQueue &Q,ElemType x){
LinkNode *s=(LinkNode *)mallloc(sizeof(LinkNode));
s->data=x;
s->next=NULL;
Q.rear->next=s; //新結點插入到rear 之後
Q.rear=s; //修改表尾指標
}
不帶頭結點 新元素入隊,由於開始front、rear 指標都是指向 NULL 的,所以需要對其進行修改操作:
void EnQueue(LinkQueue &Q,ElemType x){
LinkNode *s=(LinkNode *)mallloc(sizeof(LinkNode));
s->data=x;
s->next=NULL;
if(Q.front==NULL){ //在空佇列中插入第一個元素,需要特殊處理
Q.front=s; //修改隊頭隊尾指標
Q.rear=s;
}else{
Q.rear->next=s; //新結點插入到 rear 結點之後
Q.rear=s; //修改 rear 指標
}
}
3,出隊
帶頭結點隊頭元素出隊操作,邏輯比較簡單,直接見程式碼:
bool DeQueue(LinkQueue &Q,ElemType &x){
if(Q.front==Q.rear)
return false; //空隊
LinkNode *p=Q.front->next;
x=p->data; //用變數x 返回隊頭元素
Q.front->next=p->next; //修改頭結點的next 指標
if(Q.rear==p) //此次是最後一個結點出隊
Q.rear=Q.front; //修改rear 指標
free(p); //釋放結點空間
return true;
}
不帶頭結點 隊頭元素出隊,程式碼如下:
bool DeQueue(LinkQueue &Q,ElemType &x){
if(Q.front==NULL)
return false; //空隊
LinkNode *p=Q.front; //p指向此次出隊的結點
x=p->data; //用變數x 返回隊頭元素
Q.front=p->next; //修改front 指標
if(Q.rear==p){ //此次是最後一個結點出隊
Q.front=NULL; //front指向NULL
Q.rear=NULL; //rear指向NULL
}
free(p);
return true;
}
最後,我們一定要多動手、勤思考,這樣我們才有可能攻陷資料結構!