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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

栈的简单实现及应用

發布時間:2023/12/20 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 栈的简单实现及应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

棧的簡單實現及其應用

  • 什么是棧?
  • 棧的分類
  • 棧的數據結構
  • 棧的基本操作
    • 棧的初始化
    • 棧的銷毀
    • 入棧操作
    • 出棧和棧空的判斷
    • 獲取棧頂元素
    • 獲取棧的元素個數
    • 頭文件
  • 總結
  • 棧的應用

什么是棧?

:一種特殊的線性表,其只允許在固定的一端進行插入和刪除元素操作。進行數據插入和刪除操作的一端稱為棧頂,另一端稱為棧底。棧中的數據元素遵守后進先出LIFO(Last In First Out)的原則。
入棧:棧的插入操作叫做進棧/壓棧/入棧;(從棧頂插入數據)
出棧:棧的刪除操作叫做出棧;(也是從棧頂刪除數據)

做個簡單的比喻:我們的就相當于一個彈夾,入棧操作就相當向彈夾之中壓入子彈;出棧操作就相當將彈夾里面的子彈取出來;而這個壓入子彈和取出子彈都只有一個口;這個口就是棧頂,出入數據都從棧頂進行;

壓棧和出棧:

棧的分類

棧主要分為兩類
1、順序棧;(用順序表實現的棧)
2、鏈式棧;(用鏈表實現的棧)

日常情況下,我們通常會選用順序表來實現棧?
為什么?
因為如果我們將順序表的尾作為棧頂的話,入棧(尾插)、出棧(尾刪)效率很高就是O(1);
當然理論上我們也可以將順序表的頭作為棧頂,但是沒人會這樣高,因為入棧(頭插)、出棧(頭刪)效率很低,時間復雜度是O(N^2)是一種事倍功半的操作;

那既然這樣的話,我們為什么不選單鏈表的頭做為棧頂,這樣的話入棧(頭插)、出棧(頭刪)效率也很高啊,時間復雜度也是O(1);
那么為什么不選它嘞?
我們目前來看是這樣,但是具體實現起來,我們就得用一個棧頂指針來維護這個棧,那么就需要考慮很多特俗情況,同時參數這塊也需要傳二級指針,因為我的棧頂指針(鏈表頭節點指針)是會變的;

而如果利用順序表實現的話,就不會存在傳二級指針的問題,也不需要考慮過多的特殊情況;
為此我們強烈推薦利用順序表來實現棧!!!

棧的數據結構

為了維護這一個棧,我們利用一個結構體來維護棧;

這里我們只需與順序表區分一下top指針,top指針是專門用來維護棧頂元素的;

棧的基本操作

棧的初始化

剛開是一定是空棧,這點毋庸置疑;
所以我們結構體里面的nums置空,容量capcity也應該置空;
但是這里的top指針有兩種初始化方式
1、top=-1;
2、top=0;
這兩種初始化方式對應著后面的操作有些不同;
如果top初始化為-1,那么我的棧頂元素是那個?
是不是就是nums[top],我的top每次都是指向上一次的空間的,為此我們每一次入棧,都要先將top++,在將數據放入top所指位置;為此我的top指針就指向最后一個空間(棧頂位置);
如果我的top指針初始化為0的話,我就不需要先++top了,直接在top位置插入數據就可以了,因為此時top位置就是待插入數據的位置,為此我們數據插入完畢過后需要++top,而此時的top表示的是棧頂元素的下一個位置top-1才表示棧頂元素的位置,這時的top也就相當于順序表里面元素個數也就相當于順序表里面的size;
本文采用top=0;的初始化方式:

//初始化棧 void InitStack(ST* ps) {assert(ps);//防止亂傳ps->capcity = 0;ps->top = 0;ps->nums = NULL; }

棧的銷毀

銷毀與初始化操作通常都是連載一起的,我們在初始化操作實現完成過后便可直接實現銷毀操作:

//銷毀棧 void DestroyStack(ST*ps) {assert(ps);ps->capcity = 0;ps->top = 0;free(ps->nums); }

入棧操作

入棧操作對于順序表來說就是尾插,既然是要插入數據,我們就要首先檢查一下容量夠不夠,不免容量不夠而出現插入失敗的情況:
檢查容量

//檢查擴容,不提供給用戶,由程序自己完成 static void Check_Capcity(ST* ps) {assert(ps);if (ps->capcity == ps->top)//需要擴容{int len = (ps->capcity == 0) ? 4 : ps->capcity * 2;DataType* tmp = (DataType*)realloc(ps->nums,len*sizeof(DataType));if (!tmp){printf("realloc fail!\n");exit(EXIT_FAILURE);}ps->nums = tmp;ps->capcity = len;} }

當然這個檢查容量的操作是由程序自己完成的,我們使用者是用不到的,為此我們可以將這個函數加以static關鍵字加以修飾,以此切斷它的外部鏈接屬性,不把它暴露給用戶,加強其封裝性!!!
容量問題現在解決了,接下來便是插入數據(入棧):

//入棧 void StackPush(ST* ps,DataType x) {assert(ps);Check_Capcity(ps);ps->nums[ps->top] = x;ps->top++; }

出棧和棧空的判斷

當然出棧的操作,必然伴隨著數據的刪除,為此我們必須保證有數據可刪才行!!數據都沒了,那還刪個der!為此我們需要先實現一下棧的判空
由于我么初始化的時候選擇的是將top初始化為0,那么top的數據就是元素個數,為此top==0是便是棧為空,我們便返回真;棧不為空,便返回假:

//判斷棧是否為NULL bool StackEmpty(ST* ps) {assert(ps);return !ps->top; }

接下來我們保證了數據有的刪,那我們直接移動top指針就行了,讓計算機訪問不到,不用真的刪除并釋放,同時free也不支持只釋放不完整的空間;

//出棧 void StackPop(ST* ps) {assert(ps);assert(!StackEmpty(ps));//判空ps->top--; }

獲取棧頂元素

同理可得,我們獲取元素,那也得要棧里面有元素才行,棧都為空,就沒必要再去取元素了:
為此,我們首先要先判斷一下棧是不是為NULL:

//獲取棧頂元素 DataType StackTop(ST* ps) {assert(ps);assert(!StackEmpty(ps));//棧不為空,我們才有元素獲取;return ps->nums[ps->top - 1]; }

獲取棧的元素個數

我們初始化的時候top初始化的0,為此,我們top所表示的意義和元素個數等價,為此我們只需返回top即可:

//統計棧的元素 size_t StackSize(ST* ps) {assert(ps);return ps->top; }

頭文件

#pragma once #include<stdio.h> #include<stdbool.h> #include<stdlib.h> #include<assert.h> typedef char DataType; typedef struct Stack {DataType* nums;int capcity;int top; }ST; //初始化棧 void InitStack(ST* ps); //銷毀棧 void DestroyStack(ST* ps); //入棧 void StackPush(ST* ps,DataType x); //出棧 void StackPop(ST*ps); //判斷棧是否為NULL bool StackEmpty(ST* ps); //統計棧的元素 size_t StackSize(ST* ps); //獲取棧頂元素個數 DataType StackTop(ST*ps);

總結

或許有的讀者會疑惑,我們為什么要單獨對棧頂元素和棧的元素個數專門寫個函數呢?我們直接一句printf不就搞定了嘛。比如:我直接printf(“%d\n”,st.top);不就是棧的元素嘛,為啥還要專門寫個函數;
我們現在是知道top是初始化為0,然后top就直接就表示元素個數,那如果我初始化top為-1,那么此時我的top就不再等價表示順序表元素個數了,top+1才是表示元素個數,這也就意味著我需要去查看源碼到底是怎么初始化top的,很是麻煩,而且難免有時候看錯😊😊😊,而我們直接使用函數接口去求棧的元素個數,則不需要關心top是怎么初始化的,直接調就行了,很是方便,也不會出錯!!就問這兩種方式你更青睞與哪一種,我比較喜歡第二種!!😊😊😊

棧的應用

上面介紹了什么是棧,同時也介紹了棧的基本操作,下面我們就來做上一道題,感受一下棧的威力!!
題目描述:

??挑戰鏈接??

分析:
思路:
我們可以利用棧先進后出的特點:
遇到左括號就進棧,遇到右括號就獲取棧頂元素,并且出棧;
舉個例子:

為此我們來寫我們的第一次代碼:
由于C語言沒有棧,所以我們需要將我們寫的棧copy過去,才行:


運行失敗!!
通過測試用例,我們在走讀代碼,發現當只有左括號的時候,就會出現一直進棧,沒有出棧的操作,自然也就不會走到false位置,最后直接就會來到treu位置,為此我們通過測試用例可以相當,如果全部能夠匹配成功的話,那么我的棧最后一定是沒有剩余元素的,如果有,那么說明一定還存在左括號沒有匹配成功;為此我們的代碼改進如下:

再次運行:

還是失敗!!
這次他給我們報的測試用例是全是右括號的情況,假設全是右括號,按照上面的邏輯,我就需要去進行出棧操作,可是由于全是右括號,沒有左括號給我們入棧,棧里面自然也就沒有元素給我們出,這種情況就是我遇到了右括號,但是沒有左括號與我匹配,我直接就返回false;
代碼改進:

這次代碼就過了:

時間復雜度:O(N)
空間復雜度:O(N)

具體代碼:

typedef char DataType; struct ListNodes {DataType val;struct ListNodes* next; }; typedef struct Stack {int size;struct ListNodes* Head; }ST; //初始化棧 void InitStack(ST*ps); //銷毀棧 void DestroyStack(ST* ps); //入棧 void StackPush(ST*ps,DataType x); //出棧 void StackPop(ST*ps); //統計棧里元素個數 size_t StackSize(ST*ps); //判斷棧是否為NULL bool StackEmpty(ST* ps); //獲取棧頂元素 DataType StackTop(ST* ps); //初始化棧 void InitStack(ST* ps) {assert(ps);ps->Head = NULL;ps->size = 0; } //銷毀棧 void DestroyStack(ST* ps) {assert(ps);struct ListNodes* cur = ps->Head;while (cur){struct ListNodes* next = cur->next;free(cur);cur = next;}ps->Head = NULL;ps->size = 0; } //創建節點,程序自動創建,用戶無需關心 static struct ListNodes* BuyListNode(DataType x) {struct ListNodes* NewNode = (struct ListNodes*)malloc(sizeof(struct ListNodes));if (NewNode == NULL){printf("malloc fail!\n");exit(EXIT_FAILURE);}NewNode->next = NULL;NewNode->val = x;return NewNode; } //入棧 void StackPush(ST* ps, DataType x) {assert(ps);struct ListNodes* NewNode = BuyListNode(x);struct ListNodes* next = ps->Head;NewNode->next = next;ps->Head = NewNode;ps->size++; } //出棧 void StackPop(ST* ps) {assert(ps);assert(!StackEmpty(ps));struct ListNodes* next = ps->Head->next;free(ps->Head);ps->size--;ps->Head = next; } //判斷棧是否為NULL bool StackEmpty(ST* ps) {assert(ps);return ps->size == 0; } //統計棧里元素個數 size_t StackSize(ST* ps) {assert(ps);return ps->size; } //獲取棧頂元素 DataType StackTop(ST* ps) {assert(ps);assert(StackEmpty(ps)==false);return ps->Head->val; }bool isValid(char* s) {ST st;InitStack(&st);while (*s){//1、左括號進棧if ((*s == '[') || (*s == '(') || (*s == '{')){StackPush(&st, *s);s++;}else//2、右括號直接出棧{if (StackEmpty(&st))//表示棧里還沒有元素,但是我的s指向右括號,無法與我的右括號匹配 {DestroyStack(&st);return false;}char top = StackTop(&st);StackPop(&st);//3、開始比較右括號與左括號匹不匹配//匹配成功if (((top == '[') && (*s == ']')) || ((top == '{') && (*s == '}')) || ((top == '(') && (*s == ')')))s++;else//匹配失敗{printf("top==%c s==%c\n", top, *s);DestroyStack(&st);//先銷毀一下棧,在返回避免造成內存泄漏return false;}}}if (StackEmpty(&st) == false)//棧不為空,表示里面還有左括號為匹配成功,直接返回false;{DestroyStack(&st);return false;}DestroyStack(&st);return true; }

總結

以上是生活随笔為你收集整理的栈的简单实现及应用的全部內容,希望文章能夠幫你解決所遇到的問題。

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