本章節開始數據結構第二篇,棧和隊列:
棧:算法
隊列:數組
咱們把相似於彈夾那種先進後出的數據結構稱爲棧,棧是限定僅在表尾進行插入和刪除操做的線性表,咱們把容許插入和刪除的一端稱爲棧頂,另外一端稱爲棧底,不含任何數據元素的棧稱爲空棧,棧又稱後進後出的線性表,簡稱LIFO結構。數據結構
棧首先是一個線性表,也就是說,棧元素具備線性關係,即前驅後繼關係,只不過它是一種特殊的線性表而已。app
棧的特殊之處在於限制了這個線性表的插入和刪除位置,它始終只在棧頂進行。這也就使得:棧底是固定的,最早進棧的只能在棧底。ui
棧的插入操做,叫作進棧;棧的刪除操做叫作出棧。spa
用來存放棧的數據元素對應的數據存儲結構稱爲棧的存儲結構。.net
棧是線性表的特例,因此棧的順序存儲結構其實就是線性表順序存儲結構的簡稱,咱們簡稱爲順序棧。線性表是用數組來實現的,對於棧這種只能一頭插入刪除的線性表來講,用數組下標爲0(棧底不變,只須要跟蹤棧頂的變化便可)的一端做爲棧底比較合適。
指針
順序棧定義以下:code
typedef struct
{
int data[maxsize]; //定義一個數組大小爲maxsize的數組,用來存放棧中數據元素
int top; //棧頂指針
}SqStack; //順序棧定義
複製代碼
把棧頂放在單鏈表的頭部,用鏈表來存儲棧的的數據結構稱爲鏈棧。
orm
鏈棧結點定義以下:
typedef struct LNode
{
int data; //數據域
struct LNode *next; //指針域
}LNode; //鏈棧結點
複製代碼
對於順序棧,一共有4個要素,包括兩個特殊狀態和兩個操做。
特殊狀態:
1)棧空狀態:st.top == -1
,也有的用st.top = 0
表示棧空,這個時候棧頂位置爲0。
2)棧滿狀態:st.top == maxsize-1
表示棧滿。maxsize爲棧中最大元素個數,maxsize-1爲棧滿時棧頂元素在數組中的位置,因數組位置是從0開始的。
操做:
順序棧的進棧和出棧操做都是在棧頂進行的,因此只須要更改棧頂位置便可達到進棧和出棧的目的。
1)初始化棧:
void initStack(SqStack &st) //初始化棧
{
st.top = -1; //棧頂指針設置爲-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)出棧操做:
出棧與進棧是相對應的操做
int push(SqStack &st,int x)
{
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++的區別*/
top = 1
a = ++top
b = top++
a = 2
b = 1
複製代碼
與順序棧對應,鏈棧也有4個元素,包括兩個狀態和兩個操做。
狀態:
1)棧空:lst -> next == NULL
,即棧沒有後繼結點時,棧爲空。
2)棧滿:若是存儲空間無限大的話,不會存在棧滿的狀況。
操做:
鏈棧的進棧就是頭插法創建鏈表的插入操做;出棧就是單鏈表的刪除操做。
void initStack(LNode *&lst)
{
lst = (LNode*)malloc(sizeof(LNode)); //製造一個頭結點
lst -> next = NULL; //初始頭結點指向爲NULL
}
複製代碼
2)進棧:
void push(LNode *lst,int x)
{
LNode *p;
p = (LNode*)malloc(sizeof(LNode)); //爲進棧元素申請結點空間
p -> next =NULL; //初始化結點不指向任何元素
/*進棧,至關於鏈表的頭插法*/
p -> data = x; //將x賦值給p結點的值域
p -> next = lst -> next; //p指針指向原lst指向的結點
lst -> next = p; //lst指向結點p
}
複製代碼
3)出棧:
int pop(LNode *lst,int &x)
{
LNode *p;
if(lst -> next == NULL) //棧空則不能出棧,返回0;而棧不會滿,因此在進棧的時候未做判斷
return 0;
/*出棧,至關於鏈表的刪除結點*/
p = lst -> next;
x = p -> data;
lst -> next = p -> next;
free(p);
return 1;
}
複製代碼
4)簡化版操做:
/*元素(指針p所指)進棧操做*/
/*相似於頭插法創建鏈表*/
p -> next = lst -> next; //將空棧的頭結點指向p
lst -> next = p; //將指針p指向空棧頭結點
/*出棧操做(出棧元素保存在x中)*/
/*相似於單鏈表的刪除操做*/
p = lst -> next;
x = p -> data;
lst -> next = p -> next;
free(p);
複製代碼
隊列是隻容許在一端進行插入操做,而在另外一端進行刪除操做的線性表,隊列是一種先進先出的線性表,簡稱FIFO,容許插入的一端稱爲隊尾(Rear),容許刪除的一端稱爲隊頭(Front)。向隊中插入元素稱爲進隊,新元素進隊後成爲新的隊尾元素;向隊中刪除元素稱爲出隊,元素出隊後,其後繼元素就成爲新的隊頭元素。
用來存儲隊列數據元素的數據結構。
使用順序表存儲隊列時,隊列元素的出隊是在隊頭,即下標爲0的地方,當有元素出隊時,出隊元素後面的全部元素都須要向前移動,保證隊列的隊頭始終處在下標爲0的位置,此時會大大增長時間複雜度。
typedef struct
{
int data[maxsize]; //定義數組
int front; //隊首指針
int rear; //隊尾指針
}SqQuene; //順序隊列定義
複製代碼
用鏈表來存儲隊列元素的數據結構稱爲隊列的鏈式存儲結構,定義以下:
typedef struct QNode
{
int data; //數據域
struct QNode *next; //指針域
}QNode; //隊結點類型定義
複製代碼
鏈隊類型定義:
typedef struct
{
QNode *front; //隊頭指針
QNode *rear; //隊尾指針
}LiQuene; //鏈隊類型定義
複製代碼
由於順序隊列出隊時時間複雜度較高,有問題老是要解決的,爲何必定要讓隊頭出如今下標爲0的位置呢?因此有人提出了不去限制隊列元素必須存儲在數組的前n個單元這一條件,這樣隊頭元素就不須要必定在下標爲0的位置。可是隨着隊列元素的出隊,隊頭指針在向後移動,假設隊尾指針已經在maxsize-1的位置,這個時候雖然隊列還有存儲空間,可是隊尾已經沒法進隊了,好比下圖這樣:
循環隊列的要素:
兩個狀態:
隊空狀態:
qu.rear = qu.front
複製代碼
(qu.rear+1)%maxsize == qu.front
複製代碼
兩個操做:
元素x進隊操做(移動隊尾指針)
qu.rear = (qu.rear+1)%maxSize;
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;
}
複製代碼
鏈隊就是採用鏈式存儲結構存儲隊列。鏈隊的四個要素:隊空和隊滿,元素進隊和出隊操做。
lqu -> rear == NULL; or lqu -> front == NULL
複製代碼
隊滿狀態:
通常來講不存在隊滿的狀況,只要內存足夠大。
元素進隊操做(指針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; //若是隊列爲空,則新結點既是隊尾結點也是隊首結點
else
{
lqu -> rear -> next = p; //將新結點連接到隊尾,rear指向該結點
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) //隊列中只有一個結點時的出隊操做
lqu -> front = lqu -> rear =NULL
else
lqu -> front = lqu -> front -> next;
x = p -> data;
free(q);
return 1;
}
複製代碼