功能受限的表結構
一、棧和佇列介紹
- 棧和佇列是兩種重要的線性結構,從資料結構角度,他們都是線性表,特殊點在於它們的操作被限制,也就是所謂的功能受限,統稱功能受限的線性表
- 從資料型別角度,它們也可以是看成處理、管理資料的一種規則
二、棧結構
-
棧(stack)是限定在表尾進行資料的插入、刪除等操作的線性表(只允許操作一個埠的資料)
-
表尾稱為棧頂,表頭稱為棧底 ,當沒有元素的空表稱為空棧,當元素的數量到達棧的容量時稱為滿棧 ,新增資料到棧頂中的動作稱為入棧、壓棧,把資料從棧頂中拿出的動作稱為出棧、彈棧,正因為這個資料的新增、刪除的規則,所以棧中元素滿足先進後出,簡稱FILO表、LIFO
-
棧結構可以具備的功能
-
建立
-
銷燬
-
是否滿棧
-
是否空棧
-
入棧
-
出棧
-
檢視棧頂元素
-
檢視元素數量
注意:只有順序棧才有需要判斷棧是否滿
-
1、棧結構的順序實現
// 設計順序棧結構
typedef struct ArrayStack {
TYPE* ptr; // 儲存棧元素的記憶體首地址
size_t cap; // 棧的容量
size_t top; // 棧頂的位置
} ArrayStack;
程式碼實現
2、棧結構的鏈式實現
#define TYPE int
typedef struct ListNode {
TYPE data;
struct ListNode* next;
} ListNode;
// 鏈式棧結構
typedef struct ListStack {
ListNode* top; // 棧頂指標 指向棧頂節點
size_t size; // 節點數量
} ListStack;
程式碼實現
3、棧的應用
- 記憶體管理,例如棧記憶體,之所以叫棧記憶體因為它遵循棧的先進後出原則,函式呼叫、函式引數的傳參、定義,先把資料入棧,等結束時,逆序出棧,函式的呼叫、結束跳轉也是遵循棧結構原則
- 特殊的演算法:算術表示式的轉換(中綴表示式轉字尾表達) 、進位制轉換、迷宮演算法
三、佇列結構
1、佇列介紹
- 與棧結構相似的是,也只允許在埠處進行新增、刪除操作,但是有兩個埠,一個負責新增資料,稱為入隊 ,該埠稱為隊尾,另一個埠只負責刪除資料,稱為出隊,該埠稱為隊頭,屬於一種先進先出結構,稱為FIFO
2、佇列所具備的功能
- 建立佇列
- 銷燬佇列
- 判斷隊空
- 判斷隊滿 (只有順序儲存時才有)
- 入隊
- 出隊
- 檢視隊頭元素
- 檢視隊尾元素
- 佇列元素數量
3、佇列的鏈式實現
#define TYPE int
typedef struct ListNode {
TYPE data;
struct ListNode* next;
} ListNode;
// 設計鏈式佇列結構
typedef struct ListQueue {
ListNode* front; // 隊頭
ListNode* rear; // 隊尾
size_t size; // 節點數量
} ListQueue;
程式碼實現
4、佇列的順序實現
- 順序佇列的隊尾下標rear會隨著入隊而增大rear+1,隊頭下標front會隨著出隊增大front+1,因為是順序結構,就有隨著入隊和出隊的進行,可能超出有效的下標範圍,如果不進行處理,那麼佇列無法重複使用。
- 為了避免這種情況,當隊尾、隊頭下標達到儲存空間的末尾時,要想辦法讓它們回到記憶體的開頭位置,相當於把記憶體想象成一個環形,從而可以迴圈使用佇列,這樣的佇列稱為迴圈佇列
- 因此當隊尾、隊頭下標增加時,都要對佇列的容量求餘
- rear = (rear+1)%cap
- front = (front+1)%cap
帶計數器版本的迴圈佇列
- 很直接地解決了元素數量的問題
- 可以直接解決隊空、隊滿的判斷矛盾問題
- 但是在佇列結構中會多增加一個資料項,並且每次入隊、出隊操作都要對其進行修改
typedef struct ArrayQueue {
TYPE* ptr; // 儲存元素的記憶體首地址
size_t cap; // 容量
size_t cnt; // 元素個數 計數器
int front; // 隊頭下標
int rear; // 隊尾下標
} ArrayQueue;
程式碼實現
不帶計數器的版本
程式碼實現