勤於總結,持續輸出!
1.棧
1.1棧的定義
棧(stack)是限定在表尾進行插入和刪除的操作的線性表。
我們把允許插入和刪除的一端稱為棧頂(top),另一端稱為棧底(bottom),不包含任何數據元素的棧稱為空棧。棧又稱為后進先出(Last In First Out)的線性表,簡稱LIFO結構。
棧的插入操作,叫做進棧,也稱壓棧、入棧。
棧的刪除操作,叫做出棧,也稱彈棧。
1.2棧的順序存儲結構及實現
既然棧是線性表的特例,那么棧的順序存儲其實也是線性表順序存儲的簡化。
用數組實現,下標為0的一端作為棧底比較好,因為首元素都存在棧底。
棧的結構定義:
定義一個top變量來指示棧頂元素在數組中的位置,若存儲棧的長度為SackSize,則棧頂位置top必須小於SackSize。當棧存在一個元素時,top等於0,因此通常把空棧的判定條件為top=-1。
typedef int SElemType;
typedef struct
{
SElemType data[MAXSIZE];
int top; /*用於棧頂指針*/
} SqStack;
1.3棧的順序存儲結構——進棧操作
代碼實現:
#define MAXSIZE 5
#define OK 1
#define ERROR 0
/*插入元素e為新的棧頂元素*/
Status Push(SqStack *S, SElemType e)
{
if (S->top == MAXSIZE - 1) /*棧滿*/
{
return ERROR;
}
S->top++;
S->data[S->top] = e;
return OK;
}
測試代碼:
int main()
{
SqStack stack = { {1,2},1 }; /*初始化棧內有兩個元素,top=1*/
Push(&stack, 3);
}
運行結果:
1.4棧的順序存儲結構——出棧操作
代碼實現:
#define MAXSIZE 5
#define OK 1
#define ERROR 0
/*若棧不為空,則刪除S的棧頂元素,用e返回其值*/
Status Pop(SqStack *S, SElemType *E)
{
if (S->top == -1)
{
return ERROR;
}
*E = S->data[S->top];
S->data[S->top] = NULL;
S->top--;
return OK;
}
測試代碼:
int main()
{
SElemType e;
SqStack stack = { {1,2},1 }; /*初始化棧內有兩個元素,top=1*/
Push(&stack, 3);
Pop(&stack, &e);
}
運行結果:
驗證了棧是后進先出的結構。
1.5棧的鏈式存儲結構及實現
棧的鏈式存儲結構,簡稱為棧鏈。由於單鏈表有頭指針,而棧頂指針也是必須的,那么便可以讓它倆合二為一。以為就是說棧頂放在單鏈表的頭部。
對於鏈棧來說,基本不存在棧滿的情況,除非內存已經沒有可以使用的空間。但是對於空棧來說,鏈表原定義是頭指針指向空,那么鏈棧的空其實就是top=NULL。
棧鏈的結構代碼如下:
typedef int SElemType;
typedef struct StackNode
{
SElemType data;
struct StackNode *next;
} StackNode;
typedef struct StackNode *LinkStackPtr;
typedef struct LinkStatck
{
LinkStackPtr top;
int count;
}LinkStatck;
1.6棧的鏈式存儲結構——進棧操作
代碼實現:
#define OK 1
#define ERROR 0
typedef int Status;
typedef int SElemType;
/*插入元素e為新的棧頂元素*/
Status Push(LinkStatck *S, SElemType e)
{
LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode));
s->data = e;
s->next = S->top;
S->top = s; /*將新的節點s賦值給棧頂指針*/
S->count++;
return OK;
}
測試代碼:
int main()
{
LinkStatck stack = { NULL ,0}; /*初始化一個空鏈棧*/
Push(&stack, 1);
Push(&stack, 2);
Push(&stack, 3);
}
運行結果:
動畫模擬:
1.7棧的鏈式存儲結構——出棧操作
代碼實現:
#define OK 1
#define ERROR 0
typedef int Status;
typedef int SElemType;
/*若棧不為空,則刪除S的棧頂元素,用e返回其值*/
Status Pop(LinkStatck *S, SElemType *e)
{
LinkStackPtr p;
if (S->count == 0)
{
return ERROR;
}
*e = S->top->data;
p = S->top; /*將棧頂節點賦值給p*/
S->top = S->top->next; /*使棧頂指針下移一位,指向后一節點*/
free(p); /*釋放節點p*/
S->count--;
return OK;
}
測試代碼:
int main()
{
LinkStatck stack = { NULL ,0}; /*初始化一個空鏈棧*/
Push(&stack, 1);
Push(&stack, 2);
Push(&stack, 3);
SElemType e;
Pop(&stack, &e);
Pop(&stack, &e);
Pop(&stack, &e);
}
運行結果:
動畫模擬:
2.隊列
2.1隊列的定義
隊列(queue)是只允許在一端進行插入操作,而在另一端進行刪除操作的線性表。
隊列是一種先進先出(First In First Out)的線性表,簡稱FIFO。允許插入的一端稱為隊尾,允許刪除的一端稱為隊頭。
2.2隊列的順序存儲結構
我們假設一個隊列有n個元素,則順序存儲的隊列需建立一個大於n的數組。
現在進行入隊操作,就是在隊尾插入一個元素,不需要移動任何元素,因此時間復雜度是O[1]。
出隊操作是在隊頭,那么隊列中所有的元素都要向前移動一個位置,確保下標為0的位置不為空,時間復雜度是O[n],這是個問題。
如果不限定出隊操作時所有的元素都要向前移動,也就是說隊頭不一定必須在下標為0 的位置,出隊的性能就會大大增加。
但是這樣又會出現另一個問題——假溢出,就是假設隊列前面的位置是空着的,但是從隊尾入隊已經滿了。
循環隊列可以解決這一個問題,后面滿了,就從頭再開始,也就是頭尾相接的循環,這種頭尾相接的順序存儲結構稱為循環隊列。
但是循環隊列還是會面臨着數組溢出的問題。
2.3隊列的鏈式存儲結構及實現
隊列的鏈式存儲結構,其實就是線性表的單鏈表,只不過它能尾進頭出而已,簡稱鏈隊列。
隊頭指針指向鏈隊列的頭節點,而隊尾指針指向終端節點:
空隊列時都指向頭節點:
鏈隊列的結構如下:
typedef int QElemType;
typedef struct QNode /*結點結構*/
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct /*隊列的鏈表結構*/
{
QueuePtr front, rear; /*隊頭、隊尾指針*/
} LinkQueue;
2.4隊列的鏈式存儲結構——入隊操作
入隊操作,在隊尾插入新元素。
代碼實現:
#define OK 1
#define ERROR 0
typedef int Status;
/*插入元素e為Q的新的隊尾元素*/
Status EnQueue(LinkQueue *Q, QElemType e)
{
QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
s->data = e;
s->next = NULL;
Q->rear->next = s; /*把擁有元素e新節點s賦值給原隊尾結點的后繼*/
Q->rear = s; /*把s設置為隊尾結點,rear指向s*/
return OK;
}
測試代碼:
int main()
{
/*頭結點*/
QueuePtr head = (QueuePtr)malloc(sizeof(QNode));
head->data = 0;
head->next = NULL;
LinkQueue q = { head ,head }; //空隊列,隊頭、隊尾指針都指向頭結點
EnQueue(&q, 1);
EnQueue(&q, 2);
}
運行結果:
動畫模擬:
2.4隊列的鏈式存儲結構——出隊操作
代碼實現:
#define OK 1
#define ERROR 0
typedef int Status;
/*若隊列不為空,刪除Q的隊頭元素,用e返回其值*/
Status DeQueue(LinkQueue *Q, QElemType *e)
{
QueuePtr p;
if (Q->front == Q->rear)
{
return ERROR;
}
p = Q->front->next; /*將欲刪除的隊頭節點暫存給p*/
*e = p->data;
Q->front->next = p->next; /*將原隊頭結點后繼賦值給頭結點后繼*/
if (Q->rear == p) /*若隊頭是隊尾,則刪除后將rear指向頭結點*/
{
Q->rear = Q->front;
}
free(p);
return OK;
}
測試代碼:
int main()
{
/*頭結點*/
QueuePtr head = (QueuePtr)malloc(sizeof(QNode));
head->data = 0;
head->next = NULL;
LinkQueue q = { head ,head };
EnQueue(&q, 1);
EnQueue(&q, 2);
QElemType e;
DeQueue(&q, &e);
}
運行結果:
動畫模擬:
參考:《大話數據結構》
本文為博主學習感悟總結,水平有限,如果不當,歡迎指正。
如果您認為還不錯,不妨點擊一下下方的【推薦】按鈕,謝謝支持。
轉載與引用請注明出處。