日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【数据结构与算法】之栈与队列的应用和操作

發布時間:2024/5/21 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【数据结构与算法】之栈与队列的应用和操作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、概念

  • 棧(stack)又名堆棧,它是一種運算受限的線性表:
    • 限定僅在表尾進行插入和刪除操作的線性表。允許插入和刪除的一端為棧頂,另一端是棧底。
    • 向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成為新的棧頂元素。
    • 從一個棧刪除元素又稱作出棧或退棧,它是把棧頂元素刪除掉,使其相鄰的元素成為新的棧頂元素。
  • 舉個例子,向 AK-47 的彈夾那樣,最后壓(存)入的子彈最先被打出去,最開始壓入的子彈,最后才能被射出。

  • 再舉一個例子,一個空盤子,每烙好一張餅都放在盤子最上面(這是 push 壓入棧中),烙完后,盤子里堆了一疊餅,最下面的是最先烙好的。最上面的是剛烙好的,每一次吃只能從上面一張張拿,吃完一張拿下一張餅(這是 pop 出棧),直到盤子為空。

  • 棧的性質:棧是一種 LIFO(Last In First Out) 的線性表,也就是數據元素遵循后進先出的原則。
  • 棧的抽象數據類型:
ADT 棧(Stack) Data具有線性表一樣的性質。 Operationtop:獲取棧頂元素count:棧的長度isEmpty:棧內元素是否為空push(data):元素進棧pop():元素出棧removeAll():清空棧內元素 EndADT
  • 棧的分類(存儲結構):
    ① 棧的順序存儲結構:單棧、共享棧;
    ② 棧的鏈式存儲結構;
  • 共享棧:兩個順序棧共享存儲空間,棧1的棧頂在下標0處,棧2的棧頂在下標n-1處。
  • 棧的順序結構和鏈式結構區別:
    ① 順序結構需要預先估算存儲空間大小,適合數據元素變化不大的情況;
    ② 鏈式結構不需要預先估算存儲空間,適合數據元素變化較大的情況;
  • 棧和線性表的不同之處在于,棧只有進棧和出棧操作,并且遵循后進先出的規則,也就是說數據元素順序進棧,逆序出棧。棧可以實現回退操作,遞歸和四則運算等。

二、棧的操作

  • s.push(item) // 在棧頂壓入新元素
  • s.pop() // 刪除棧頂元素但不返回其值
  • s.empty() // 如果棧為空返回true,否則返回false
  • s.size() // 返回棧中元素的個數
  • s.top() // 返回棧頂的元素,但不刪除該元素

三、順序存儲棧

  • 概念
    • 順序存儲棧即物理結構是順序存儲,先開辟一塊內存空間(和順序存儲鏈表一樣有沒有),每 push 一個新元素,棧頂標記 top+1。
    • 直到開辟的空間被存滿,每 pop 一個棧頂元素,top-1,也就是下一個元素變成棧頂元素。
  • 定義數據結構:
#define ERROR 0 #define TRUE 1 #define FALSE 0 #define OK 1 #define MAXSIZE 20 /* 存儲空間初始分配量 */typedef int Status;/* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef int ElemType;/* ElemType類型根據實際情況而定,這里假設為int *//* 順序棧結構 */ typedef struct {ElemType data[MAXSIZE];int top; /* 用于棧頂指針 */ }Stack;

  • 初始化:
Status InitStack(Stack *S) {S->top = -1;return OK; }
  • 清空:
Status ClearStack(Stack *S) {S->top = -1;return OK; }
  • 獲取棧頂元素:
Status GetTop(Stack S, ElemType *e) {if (S.top == -1) return ERROR;*e = S.data[S.top];return OK; }
  • 獲取棧長度:
int StackLength(Stack S) {return S.top+1; }
  • Push:
Status PushStack(Stack *S, ElemType e) { if (S->top == MAXSIZE -1) return ERROR; S->top++; S->data[S->top] = e; return OK; }
  • Pop:
Status PopStack(Stack *S, ElemType *e) {if (S->top == -1) {return ERROR;}*e = S->data[S->top];S->top--;return OK; }
  • 輸出測試:
int main(int argc, const char * argv[]) {// 創建棧Stack S;InitStack(&S);for (int i = 0; i < 10; i++) {PushStack(&S, i);}StackPrint(S);// 出棧ElemType e;PopStack(&S, &e);printf("出棧:%d",e);StackPrint(S);// 獲取棧頂元素GetTop(S, &e);printf("棧頂:%d\n",e);// 輸出長度printf("棧長度:%d\n",StackLength(S));return 0; }0 1 2 3 4 5 6 7 8 9 出棧:90 1 2 3 4 5 6 7 8 棧頂:8 棧長度:9

四、鏈式存儲棧

  • 概念
    • 鏈式存儲棧以鏈表的形式,新入棧的節點,next 指向原來的棧頂節點,插在鏈表的最前端,成為新的棧頂(和鏈表的頭插法像不像?)。
    • 用 top 標記棧頂節點,而不是上面順序存儲的位置,每一次入棧新節點,top 指向新棧頂節點,count 也隨之 +1。出棧時,top 指向棧頂節點的 next 節點,count-1。


  • 定義:
#define ERROR 0 #define TRUE 1 #define FALSE 0 #define OK 1typedef int Status;/* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef int ElemType;/* ElemType類型根據實際情況而定,這里假設為int *//* 鏈棧的每一個節點,和單鏈表很像有沒有 */ typedef struct StackNode {ElemType data;struct StackNode *next; }StackNode;typedef struct StackNode * StackNodePtr;/* 棧結構 */ typedef struct {StackNodePtr top;int count; }LinkStack;
  • 初始化:
Status InitStack(LinkStack *S) {S->top = NULL;S->count = 0;return OK; }
  • 清空:
Status ClearStack(LinkStack *S) { if (S->top == NULL) return ERROR; StackNodePtr p; while (S->count != 0) { p = S->top; S->top = S->top->next; free(p); S->count--; } return OK; }
  • 獲取棧頂元素:
Status GetTop(LinkStack S, ElemType *e) {if (S.top == NULL) return ERROR;// if (S->count == 0) return ERROR; // 也可以*e = S.top->data;return OK; }
  • 獲取棧長度:
int StackLength(LinkStack S) {return S.count; }
  • Push:
Status PushStack(LinkStack *S, ElemType e) {// 創建新元素StackNodePtr p = (StackNodePtr)malloc(sizeof(StackNode));if (p == NULL) return ERROR;p->data = e;p->next = S->top;S->top = p;S->count++;return OK; }
  • Pop:
Status PopStack(LinkStack *S, ElemType *e) { if (S->top == NULL) return ERROR; // if (S->count == 0) return ERROR; // 也可以 /* 將棧頂指針指向新的棧頂 */ StackNodePtr temp = S->top; S->top = S->top->next; *e = temp->data; free(temp); S->count--; return OK; }
  • 輸出測試:
nt main(int argc, const char * argv[]) {// 創建棧LinkStack S;InitStack(&S);for (int i = 0; i < 10; i++) {PushStack(&S, i);}StackPrint(S);ElemType e;PopStack(&S, &e);printf("出棧:%d\n",e);StackPrint(S);// 獲取棧頂元素GetTop(S, &e);printf("棧頂:%d\n",e);// 輸出長度printf("棧長度:%d\n",StackLength(S));return 0; } 9 8 7 6 5 4 3 2 1 0 出棧:9 8 7 6 5 4 3 2 1 0 棧頂:8 棧長度:9

隊列

一、概念

  • 只允許在一段進行插入操作,而在另一端進行刪除操作的線性表;
  • 性質:先進先出
  • 隊列的抽象數據類型:
ADT 隊列(Queue) Data具有線性表一樣的性質。 Operationfront:隊列第一個數據count:隊列的長度isEmpty():隊列是否為空enQueue(data):數據進隊列deQueue():數據出隊列removeAll():清空隊列內的所有數據 EndADT
  • 順序隊列:就是使用數組來模擬隊列,但是數組的插入和刪除需要移動數據,比較繁瑣。
  • 循環順序隊列:在順序隊列的基礎上改造,使隊列的隊頭和隊尾可以在數組中循環變化,在數據插入和刪除就不需要頻繁移動數據了。但是順序隊列,都需要提前申請存儲空間,還有溢出的可能。
  • 鏈式隊列:為了解決順序隊列的不足,引用鏈式隊列。不需要提前申請空間,只不過會引入頻繁申請和釋放的操作。
  • 隊列有什么作用?在開發過程中,接觸最多的應該是全局并發隊列。為什么要用隊列實現呢?在線程的優先級一樣的情況下,不應該先申請系統資源的先被滿足嗎?這和在銀行排隊取錢是一個道理。

二、隊列的操作

  • push(item)
  • q.pop()
  • q.front()
  • q.back()
  • q.size()
  • q.empty()

三、鏈隊列

  • 鏈式隊列的表示:

  • 是不是似曾相識的結構?鏈棧,再看看鏈棧的表示:

  • 區別:棧,新的元素添加在棧頂,而且棧頂先出;隊列,隊尾進,隊首出。
  • 二者相反,所以,鏈隊列的操作簡單來說就是:
    • 進入隊列:Q.rear 尾節點后追加新節點,將 Q.rear 指向新節點,新節點成隊尾;
    • 出隊列:Q.front 指向的首元節點出隊列,Q.front 指向首元節點的下一個節點。
  • 先定義數據結構:
#define ERROR 0 #define TRUE 1 #define FALSE 0 #define OK 1typedef int Status;/* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef int ElemType;/* ElemType類型根據實際情況而定,這里假設為int */typedef struct QueueNode {ElemType data;struct QueueNode *next; } QueueNode, *QueueNodePtr;typedef struct {QueueNodePtr front;QueueNodePtr rear; } LinkQueue;
  • 初始化:先創建一個頭節點,讓 Q.front 和 Q.rear 指向頭節點,頭節點的 next 為 NULL:

// 初始化Status InitQueue(LinkQueue *Q) { // 初始隊列為空,只有頭節點,不是有效數據的節點 *Q->front = *Q->rear = (QueueNodePtr)malloc(sizeof(QueueNode)); if (*Q->front == NULL) return ERROR; // 頭節點的后面為空 *Q->front->next = NULL; return OK; }
  • 判斷隊列為空:當隊列為空時,恰入上圖初始化的狀態,只剩一個頭節點,此時 Q.front == Q.rear;
// 判斷是否為空Status QueueEmpty(LinkQueue Q) { if (Q.front == Q.rear) return TRUE; return FALSE; }
  • 進入隊列:
    • 進入隊列的操作,是將新元素,追加到 rear 指向的隊尾之后,rear->next = 新元素,再將 rear 指向新元素,此時,新元素成為隊尾。因為不受存儲空間限制(內存占滿另說),所以不需要一開始就判斷是否隊滿,也沒有隊滿的操作。
    • 創建新節點 p;
    • 追加到隊尾;
    • 隊列的 rear 指向 p,標記成新隊尾。

Status EnQueue(LinkQueue *Q, ElemType e) {QueueNodePtr p = (QueueNodePtr)malloc(sizeof(QueueNode));if (p == NULL) return ERROR;p->data = e;p->next = NULL;// 追加到隊尾*Q->rear->next = p;// 標記成隊尾*Q->rear = p;return OK; }
  • 出隊列:出隊列操作,是將首元節點從鏈隊刪除。
    • 判斷隊列是否為空;
    • 找到隊首節點 head(此時已拿到節點,將該節點的 data 回調出去),等待刪除;
    • 更改標記 head 后面的節點 s 為首元節點,即 head->next;
    • 判斷是否是最后一個節點,是的話,rear 也指向頭節點;
    • 釋放原首節點 head。

Status DeQueue(LinkQueue *Q, ElemType *e) {if (QueueEmpty(*Q)) return ERROR;QueueNodePtr head;// 找到要刪除的節點head = *Q->front->next;// 回調到函數外*e = head->data;// 更改頭節點*Q->front->next = head->next;// 如果刪到了隊尾最后一個元素if (*Q->rear == head)*Q->rear = *Q->front;// 刪除臨時指針指向的頭節點free(head);return OK; }
  • 清空:僅清空頭節點以外的全部節點,有頭節點在,清空完,還能繼續 EnQueue() 操作,又回到初始化后的狀態;

// 清空隊列 Status ClearQueue(LinkQueue *Q) {if (*Q->front->next == NULL) return ERROR;QueueNodePtr temp, p; // 首元節點*Q->rear = *Q->front;p = *Q->front->next;*Q->front->next = NULL;// 此時只有temp指向首元節點while (p) {temp = p;p = p->next;free(temp);}return OK; }
  • 銷毀:銷毀操作,和清空不一樣,清空僅僅刪除除頭節點以外的所有節點,清空后還可以再入隊。但是銷毀已經徹底不使用,需要連頭節點也一并 free 掉,所以代碼中是從 front 開始,而非 front->next,已 free 所有的節點;

// 銷毀隊列 Status DestoryQueue(LinkQueue *Q) {if (*Q->front->next == NULL) return ERROR;/*說明,頭節點也是個malloc開辟的,也需要釋放*/while (*Q->front) {*Q->rear = *Q->front->next;free(*Q->front);*Q->front = *Q->rear;}return OK; }
  • 獲取隊首:不更改隊列,不破壞隊列現有結構,僅查找,所以首元節的數據直接讀取 Q.front->next->data;
Status GetHead(LinkQueue Q, ElemType *e) {if (QueueEmpty(*Q)) return ERROR;*e = Q.front->next->data;return OK; }

總結

以上是生活随笔為你收集整理的【数据结构与算法】之栈与队列的应用和操作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。