数据结构—栈和队列
前言
本章節(jié)開始數(shù)據(jù)結(jié)構(gòu)第二篇,棧和隊列:
棧:
- 棧的存儲結(jié)構(gòu)
- 棧的基本操作
隊列:
- 隊列的存儲結(jié)構(gòu)
- 隊列的基本操作
棧
我們把類似于彈夾那種先進后出的數(shù)據(jù)結(jié)構(gòu)稱為棧,棧是限定僅在表尾進行插入和刪除操作的線性表,我們把允許插入和刪除的一端稱為棧頂,另一端稱為棧底,不含任何數(shù)據(jù)元素的棧稱為空棧,棧又稱后進后出的線性表,簡稱LIFO結(jié)構(gòu)。
棧首先是一個線性表,也就是說,棧元素具有線性關(guān)系,即前驅(qū)后繼關(guān)系,只不過它是一種特殊的線性表而已。
棧的特殊之處在于限制了這個線性表的插入和刪除位置,它始終只在棧頂進行。這也就使得:棧底是固定的,最先進棧的只能在棧底。
棧的插入操作,叫做進棧;棧的刪除操作叫做出棧。
1.棧的存儲結(jié)構(gòu)
用來存放棧的數(shù)據(jù)元素對應(yīng)的數(shù)據(jù)存儲結(jié)構(gòu)稱為棧的存儲結(jié)構(gòu)。
1.1棧的順序存儲結(jié)構(gòu)
棧是線性表的特例,所以棧的順序存儲結(jié)構(gòu)其實就是線性表順序存儲結(jié)構(gòu)的簡稱,我們簡稱為順序棧。線性表是用數(shù)組來實現(xiàn)的,對于棧這種只能一頭插入刪除的線性表來說,用數(shù)組下標為0(棧底不變,只需要跟蹤棧頂?shù)淖兓纯?#xff09;的一端作為棧底比較合適。
順序棧定義如下:
typedef?struct{
????int?data[maxsize];????//定義一個數(shù)組大小為maxsize的數(shù)組,用來存放棧中數(shù)據(jù)元素
????int?top;??????????????//棧頂指針
}SqStack;?????????????????//順序棧定義
復制代碼
1.2棧的鏈式存儲結(jié)構(gòu)
把棧頂放在單鏈表的頭部,用鏈表來存儲棧的的數(shù)據(jù)結(jié)構(gòu)稱為鏈棧。
鏈棧結(jié)點定義如下:
typedef?struct?LNode{
????int?data;????????????????//數(shù)據(jù)域
????struct?LNode?*next;??????//指針域
}LNode;??????????????????????//鏈棧結(jié)點
復制代碼
2.棧的操作
2.1順序棧的操作
對于順序棧,一共有4個要素,包括兩個特殊狀態(tài)和兩個操作。
特殊狀態(tài):
1)棧空狀態(tài):st.top == -1,也有的用st.top = 0表示棧空,這個時候棧頂位置為0。
2)棧滿狀態(tài):st.top == maxsize-1表示棧滿。maxsize為棧中最大元素個數(shù),maxsize-1為棧滿時棧頂元素在數(shù)組中的位置,因數(shù)組位置是從0開始的。
操作:
順序棧的進棧和出棧操作都是在棧頂進行的,所以只需要更改棧頂位置即可達到進棧和出棧的目的。
1)初始化棧:
void?initStack(SqStack?&st)????//初始化棧{
????st.top?=?-1;???????????????//棧頂指針設(shè)置為-1
}
復制代碼
2)進棧操作:
int?push(SqStack?&st,int?x){
????if(st.top?==?maxsize-1)????//判斷棧是否滿,如果滿,則不能進棧
????????return?0;
????++(st.top);????????????????//棧頂指針位置加1
????st.data[st.top]?=?x????????//x進棧,放在st.top位置
????????return?1;
}
復制代碼
3)出棧操作:
出棧與進棧是相對應(yīng)的操作
{
????if(st.top?==?-1)???????????//判斷棧是否為空,如果空,則不能進行出棧
????????return?0;
????x?=?st.data[st.top]?????//先把棧頂元素取出來
????--(st.top);?????????????????//棧頂指針位置減1
????return?1;
}
復制代碼
4)簡化版的操作:
/*初始化棧*/int?stack[maxsize];
int?top?=?-1;
/*元素x進棧*/
stack[++top]?=?x
/*元素x出棧*/
x?=?stack[top--]
/*注意++top和top++的區(qū)別*/
top?=?1
a?=?++top
b?=?top++
a?=?2
b?=?1
復制代碼
2.2鏈棧的操作
與順序棧對應(yīng),鏈棧也有4個元素,包括兩個狀態(tài)和兩個操作。
狀態(tài):
1)棧空:lst -> next == NULL,即棧沒有后繼結(jié)點時,棧為空。
2)棧滿:如果存儲空間無限大的話,不會存在棧滿的情況。
操作:
鏈棧的進棧就是頭插法建立鏈表的插入操作;出棧就是單鏈表的刪除操作。
棧的刪除操作
1)鏈棧初始化: void?initStack(LNode?*&lst)
{
????lst?=?(LNode*)malloc(sizeof(LNode));????//制造一個頭結(jié)點
????lst?->?next?=?NULL;?????????????????????//初始頭結(jié)點指向為NULL
}
復制代碼
2)進棧:
void?push(LNode?*lst,int?x){
????LNode?*p;
????p?=?(LNode*)malloc(sizeof(LNode));????//為進棧元素申請結(jié)點空間
????p?->?next?=NULL;??????????????????????//初始化結(jié)點不指向任何元素
????/*進棧,相當于鏈表的頭插法*/
????p?->?data?=?x;????//將x賦值給p結(jié)點的值域
????p?->?next?=?lst?->?next;????//p指針指向原lst指向的結(jié)點
????lst?->?next?=?p;????????????//lst指向結(jié)點p
}
復制代碼
3)出棧:
int?pop(LNode?*lst,int?&x){
????LNode?*p;
????if(lst?->?next?==?NULL)????//棧空則不能出棧,返回0;而棧不會滿,所以在進棧的時候未作判斷
????????return?0;
????/*出棧,相當于鏈表的刪除結(jié)點*/
????p?=?lst?->?next;
????x?=?p?->?data;
????lst?->?next?=?p?->?next;
????free(p);
????return?1;
}
復制代碼
4)簡化版操作:
/*元素(指針p所指)進棧操作*//*類似于頭插法建立鏈表*/
p?->?next?=?lst?->?next;????//將空棧的頭結(jié)點指向p
lst?->?next?=?p;????????????//將指針p指向空棧頭結(jié)點
/*出棧操作(出棧元素保存在x中)*/
/*類似于單鏈表的刪除操作*/
p?=?lst?->?next;
x?=?p?->?data;
lst?->?next?=?p?->?next;
free(p);
復制代碼
隊列:
隊列是只允許在一端進行插入操作,而在另一端進行刪除操作的線性表,隊列是一種先進先出的線性表,簡稱FIFO,允許插入的一端稱為隊尾(Rear),允許刪除的一端稱為隊頭(Front)。向隊中插入元素稱為進隊,新元素進隊后成為新的隊尾元素;向隊中刪除元素稱為出隊,元素出隊后,其后繼元素就成為新的隊頭元素。
1.隊列的存儲結(jié)構(gòu)
用來存儲隊列數(shù)據(jù)元素的數(shù)據(jù)結(jié)構(gòu)。
1.1隊列的順序存儲結(jié)構(gòu):
使用順序表存儲隊列時,隊列元素的出隊是在隊頭,即下標為0的地方,當有元素出隊時,出隊元素后面的所有元素都需要向前移動,保證隊列的隊頭始終處在下標為0的位置,此時會大大增加時間復雜度。
用順序表來存儲隊列元素的數(shù)據(jù)結(jié)構(gòu)稱為隊列的順序存儲結(jié)構(gòu),定義如下: typedef?struct
{
????int?data[maxsize];????????//定義數(shù)組
????int?front;????????????????//隊首指針
????int?rear;?????????????????//隊尾指針
}SqQuene;?????????????????????//順序隊列定義
復制代碼
1.2隊列的鏈式存儲結(jié)構(gòu):
用鏈表來存儲隊列元素的數(shù)據(jù)結(jié)構(gòu)稱為隊列的鏈式存儲結(jié)構(gòu),定義如下:
隊列結(jié)點類型定義: typedef?struct?QNode
{
????int?data;????????????????//數(shù)據(jù)域
????struct?QNode?*next;??????//指針域
}QNode;??????????????????????//隊結(jié)點類型定義
復制代碼
鏈隊類型定義:
typedef?struct{
????QNode?*front;????????//隊頭指針
????QNode?*rear;?????????//隊尾指針
}LiQuene;????????????????//鏈隊類型定義
復制代碼
2.隊列操作
2.1循環(huán)隊列
因為順序隊列出隊時時間復雜度較高,有問題總是要解決的,為什么一定要讓隊頭出現(xiàn)在下標為0的位置呢?所以有人提出了不去限制隊列元素必須存儲在數(shù)組的前n個單元這一條件,這樣隊頭元素就不需要一定在下標為0的位置。但是隨著隊列元素的出隊,隊頭指針在向后移動,假設(shè)隊尾指針已經(jīng)在maxsize-1的位置,這個時候雖然隊列還有存儲空間,但是隊尾已經(jīng)無法進隊了,比如下圖這樣:
雖然下標為0和1的位置處還有空間,但是隊尾已經(jīng)無法再有新元素進隊,我們把這種情況稱為“假溢出”,為了解決這種假溢出的問題,就提出了循環(huán)隊列的概念,讓隊列的頭尾進行相連,這種頭尾相連的順序存儲結(jié)構(gòu)稱為循環(huán)隊列。
循環(huán)隊列需要損失一定的空白,這樣只有在隊空的時候才會出現(xiàn)front=rear。
循環(huán)隊列的要素:
兩個狀態(tài):
隊空狀態(tài):
復制代碼
隊滿狀態(tài): (qu.rear+1)%maxsize?==?qu.front
復制代碼
兩個操作:
元素x進隊操作(移動隊尾指針)
qu.data[qu.rear]?=?x;
復制代碼
元素x出隊操作(移動隊頭指針)
qu.front?=?(qu.front+1)%maxSize;x?=?qu.data[qu.front];
復制代碼
初始化隊列算法:
void?initQueue(SqQueue?&qu){
????qu.front?=?qu.rear?=?0;隊首和隊尾指針重合,并且指向0
}
復制代碼
進隊算法:
int?enQueue(SqQueue?&qu,int?x){
????if?((qu.rear?+?1)?%?maxSize?==?qu.front)????//隊滿的判斷條件,如果隊滿則不能進隊,返回0
????????return?0;
????qu.rear?=?(qu.reat+1)%maxSize;??????????????//若隊不滿,先移動隊尾指針
????qu.data[qu.rear]?=?x;???????????????????????//元素x進隊
????return?1;
}
復制代碼
出隊算法:
int?enQueue(SqQueue?&qu,int?&x){
????if?(qu.rear?==?qu.front)????//隊空的判斷條件,如果隊空則不能出隊,返回0
????????return?0;
????qu.front?=?(qu.front+1)%maxSize;??????????????//若隊不空,先移動隊首指針
????x?=?qu.data[qu.front]?;???????????????????????//元素x出隊
????return?1;
}
復制代碼
2.2鏈隊:
鏈隊就是采用鏈式存儲結(jié)構(gòu)存儲隊列。鏈隊的四個要素:隊空和隊滿,元素進隊和出隊操作。
隊空狀態(tài): lqu?->?rear?==?NULL;?or?lqu?->?front?==?NULL
復制代碼
隊滿狀態(tài):
一般來說不存在隊滿的情況,只要內(nèi)存足夠大。
元素進隊操作(指針p指向進隊元素)
lqu?->?rear?->?next?=?p;lqu?->?rear?=?p;
復制代碼
元素出隊操作(x存儲出隊元素)
p?=?lqu?->?front;lqu?->?front?=?p?->?next;
x?=?p?->?data;
free(p);
復制代碼
初始化鏈隊算法
void?initQueue(LiQuene?*&lqu){
????lqu?=?(LiQueue*)malloc(sizeof(LiQueue));
????lqu?->?front?=?lqu?->?rear?=?NULL;
}
復制代碼
判斷隊空算法
int?isQueueEmpty(LiQueue?*lqu){
????if(lqu?->?rear?==?NULL?||?lqu?->?front?==?NULL)
????????return?1;
????else
????????return?0;
}
復制代碼
入隊算法
void?enQueue(LiQueue?*lqu,int?x){
????QNode?*p;
????p?=?(QNode*)malloc(sizeof(QNode));
????p?->?data?=?x;
????p?->?next?=NULL;
????if(lqu?->?rear?==?NULL)
????????lqu?->?front?=?lqu?->?rear?=?p;????//如果隊列為空,則新結(jié)點既是隊尾結(jié)點也是隊首結(jié)點
????else
????{
????????lqu?->?rear?->?next?=?p;???????????//將新結(jié)點鏈接到隊尾,rear指向該結(jié)點
????????lqu?->?rear?=?p;
????}?
}
復制代碼
出隊算法
int?deQueue(LiQueue?*lqu,int?&x){
????QNode?*p;
????if(lqu?->?rear?==?NULL)????//判斷隊空,如果為空,則不能出隊
????????return?0;
????else
????????p?=?lqu?->?front;
????if(lqu?->?front?==?lqu?->?rear)????//隊列中只有一個結(jié)點時的出隊操作
????????lqu?->?front?=?lqu?->?rear?=NULL
????else
????????lqu?->?front?=?lqu?->?front?->?next;
????x?=?p?->?data;
????free(q);
????return?1;
}
復制代碼
總結(jié)
- 上一篇: 常用的几种清除float浮动的方法
- 下一篇: 聊聊ribbon的超时时间设置