數據結構之棧(C語言)

數據結構之棧(C語言)

    • 1 棧的概念與結構
    • 2 棧的初始化和銷毀
      • 2.1 棧的初始化
      • 2.2 棧的銷毀
    • 3 入棧函數與出棧函數
      • 3.1 入棧函數
      • 3.2 出棧函數
    • 4 取棧頂數據,獲取數據個數 和 判空函數
      • 4.1 取棧頂數據與獲取數據個數
        • 4.1.1 取棧頂數據
        • 4.1.2 獲取數據個數
      • 4.2 判空函數
    • 5 真題實戰(有效的括號)

1 棧的概念與結構

棧是一種特殊的線性表,特殊在于其規定只允許在棧固定的一端進行數據插入和刪除操作。數據插入和刪除數據元素的一端叫做棧頂,另一端叫做棧底。棧中的元素遵從先進后出的原則。(我們可以把棧看成槍的彈夾,先壓入的子彈后擊發,后壓入的子彈先擊發)
壓棧:棧的插入操作叫做壓棧/進棧/入棧,入數據在棧頂。
出棧:棧的刪除操作叫做出棧,出數據也在棧頂。

本章使用數組來實現棧。
在這里插入圖片描述
代碼定義如下:

typedef int STDatatype;//對棧中存入的元素類型進行重命名,避免修改時誤操作typedef struct Stack
{STDatatype* c;//所存入棧中的數據int top;//表示指向棧頂元素位置or棧頂數據的下一個位置int capacity;//表示棧的容量
}ST;

在上面代碼中我們對在的每個節點中定義了三個變量,對于top這一項較為特殊,下文2.1中會對兩種情況進行闡述。

2 棧的初始化和銷毀

2.1 棧的初始化

我們應知在創建出一個新的棧后,這個新的棧應是一個空棧,如此也就意味著指向存儲數據的指針STDatatype* c = NULL,表示容量的int capacity = 0。但對于top我們不能輕率的認為top == 0表示空棧時top所指向的是棧頂數據,如果這么認為的話會對后面的操作造成誤導(比如對棧判空)。因為top既然在等于0時表示棧為空棧,而且top還指向著棧頂數據,那么此時在索引為0處(即top == 0處 )就應當存放有具體數據,然而此時棧卻是空的,所以當top == 0表示空棧時top就不能指向著棧頂數據,而是指向棧頂數據的下一個位置。 反之同理,若想讓top指向棧頂數據,則top == -1時才可表示空棧。(魚和熊掌不可兼得,表示空棧為魚,指向棧頂數據為熊掌)
如圖所示:
在這里插入圖片描述

//棧的初始化
void STInit(ST* pst)
{assert(pst);pst->c = NULL;//top指向棧頂數據的下一個位置pst->top = 0;//top指向棧頂數據//pst->top = -1;pst->capacity = 0;
}

我們之所以選擇top指向棧頂元素的下一個位置這種定義,一是因為在諸多程序中變量為零都是判空的條件,如此設置有助于代碼的一致性與可讀性。二是因為top如此一來就可以等同于capacity的作用,直接反映棧中元素數量。三是因為簡化了棧的判空并且減少了對棧邊界的檢查,top == 0時棧為空;最大容量為n時,top == n表示棧滿。無需額外檢查。

2.2 棧的銷毀

由于本文中的棧是使用數組完成實現,故棧的銷毀與順序表的銷毀一致。首先使用assert對數組斷言(確定傳參有效),然后釋放掉動態內存,最后將top與capacity均置為0。
代碼如下:

void STDestroy(ST* pst)
{assert(pst);free(pst->c);//釋放動態內存pst->c = NULL;//首地址置為空pst->top = pst->capacity = 0;
}

3 入棧函數與出棧函數

3.1 入棧函數

經過初始化函數對棧進行操作后,數組為空,容量為零。所以在將數據壓入棧中之前,我們要先對數組的容量進行檢測,檢查容量是否已滿。如果滿了,我們就進行擴容操作;反之,我們就直接插入即可。
對于內存函數的選擇方面,由于擴容時會存在原本棧容量已經滿的情況,此時不僅要擴大內存塊,還要保留棧中原本的數據,所以應當選擇realloc

void STPush(ST* pst, STDatatype x)
{assert(pst);//擴容if (pst->c == pst->capacity){int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDatatype* tmp = (STDatatype*)realloc(pst->c, newcapacity * sizeof(STDatatype));if (tmp == NULL){perror("realloc fail !");return;}pst->c = tmp;pst->capacity = newcapacity;}//擴容完畢后插入數據pst->c[pst->top] = x;//將數據插入到棧頂//注意這里要做出區分//此時top == 0 這個位置就是棧頂//但是top的指向是棧頂數據的下一個位置pst->top++;
}

3.2 出棧函數

出棧的操作只要在邏輯結構上將出棧數據移除即可,也就是通過索引top的加減完成出棧操作。此時有人可能會有疑問,既然其在物理結構上還存在于數組的內存塊中,那么不會對棧滿的判斷造成影響嗎?
因為我們在對top定義時,將其定義為指向棧頂數據的下一個位置,如此top==0就表示了空棧,top就表示棧中有幾個數據,所以不會對棧滿的判斷造成影響。此外,當我們進行一次出棧后,出棧的數據雖然仍存在于數組的物理結構中,但是在下一次進行壓棧操作后,剛才出棧數據的位置就會分配給新壓入棧的數據,出棧的數據被新壓入棧的數據覆蓋掉。

void STPop(ST* pst)
{assert(pst);assert(pst->top > 0);//top表示棧中數據個數//當top==0時,棧已經成空棧,不能再進行出棧操作pst->top--;
}

4 取棧頂數據,獲取數據個數 和 判空函數

4.1 取棧頂數據與獲取數據個數

4.1.1 取棧頂數據

因為top是指向棧頂數據的下一個位置,所以要想獲得棧頂數據,索引應當等于top - 1。
在這里插入圖片描述
代碼如下:

STDatatype STTop(ST* pst)
{assert(pst);//確保傳參有效assert(pst->top > 0);//確保棧不為空return pst->c[pst->top - 1];//top - 1為棧頂數據
}
4.1.2 獲取數據個數

top就代表著棧中的數據個數,所以直接返回top即可。
代碼如下:

int STsize(ST* pst)
{assert(pst);return pst->top;
}

4.2 判空函數

根據我們前面的定義,top == 0時為空棧。所以判空過程中如果top等于零就是真(true),top不等于0就是假(false)。故我們將返回值類型設置為布爾類型,便于直觀表現判空結果。
代碼如下:

bool STEmpty(ST* pst)
{assert(pst);return pst->top == 0;
}

5 真題實戰(有效的括號)

在這里插入圖片描述
輸出樣例:
在這里插入圖片描述
這個題當中我們之所以選擇用棧來解答就是因為棧獨特的后進先出的特性,根據題目要求我們可以得出每個右括號都要與最近的未閉合的左括號匹配,那么每當識別到左括號就將其壓入棧中,當識別到右括號時就將剛壓入棧的左括號出棧頂與之匹配,如果不匹配也就意味著字符串非有效。
按照上述思路我們來完成實現(前提是上文中棧的基本結構已經編寫完畢):
初階版:

//因輸出結果為true與false,故返回值類型定位布爾類型
bool isValid(char* s) {ST st;STInit(&st);while(*s){//識別括號,是左括號入棧if(*s == '(' || *s == '[' || *s == '{'){STPush(&st , *s);}//不是左括號是右括號,剛進棧的左括號出棧頂與右括號匹配else{//取棧頂元素char top = STTop(&st);//出棧頂STPop(&st);//匹不匹配只需看不匹配情況即可,如果匹配就繼續執行循環,不匹配直接結束函數if(top == '(' && *s != ')'|| top == '[' && *s != ']'|| top == '{' && *s != '}'){STDestroy(&st);//即使不匹配也要及時銷毀棧,避免內存泄漏return false;}}//每完成一次壓棧或出棧操作字符串數組索引向后移一位++s;}return true;
}

提交后發現給出的四個樣例均可通過,但當字符串中只有一個"["時,運行結果錯誤。在這里插入圖片描述
這是因為左括號數量比右括號多,當最后一個左括號被壓入棧中之后,沒有右括號與其匹配,左括號依然存在于棧中。而程序運行完并沒有對棧中進行判空,所以結果出錯。
修改后:

//因輸出結果為true與false,故返回值類型定位布爾類型
bool isValid(char* s) {ST st;STInit(&st);while(*s){//識別括號,是左括號入棧if(*s == '(' || *s == '[' || *s == '{'){STPush(&st , *s);}//不是左括號是右括號,剛進棧的左括號出棧頂與右括號匹配else{//取棧頂元素char top = STTop(&st);//出棧頂STPop(&st);//匹不匹配只需看不匹配情況即可,如果匹配就繼續執行循環,不匹配直接結束函數if(top == '(' && *s != ')'|| top == '[' && *s != ']'|| top == '{' && *s != '}'){STDestroy(&st);return false;}}//每完成一次壓棧或出棧操作字符串數組索引向后移一位++s;}//匹配完如果棧不為空,說明左括號比右括號多,數量不匹配bool ret = STEmpty(&st);STDestroy(&st);return ret;
}

再次提交發現仍然存在報錯,當字符串中只有一個"]"時,運行結果錯誤。
在這里插入圖片描述
由于沒有左括號壓入棧中,所以在取棧頂元素時就觸發了assert斷言的報錯。
故在取棧頂元素之前,也應對棧進行判空操作。
最終修改后:

//因輸出結果為true與false,故返回值類型定位布爾類型
bool isValid(char* s) {ST st;STInit(&st);while(*s){//識別括號,是左括號入棧if(*s == '(' || *s == '[' || *s == '{'){STPush(&st , *s);}//不是左括號是右括號,剛進棧的左括號出棧頂與右括號匹配else{//如果棧為空,字符串只有一個右括號if( STEmpty(&st) ){STDestroy(&st);return false;}//如果棧不為空,進行匹配//取棧頂元素char top = STTop(&st);//出棧頂STPop(&st);//匹不匹配只需看不匹配情況即可,如果匹配就繼續執行循環,不匹配直接結束函數if(top == '(' && *s != ')'|| top == '[' && *s != ']'|| top == '{' && *s != '}'){STDestroy(&st);return false;}}//每完成一次壓棧或出棧操作字符串數組索引向后移一位++s;}//匹配完如果棧不為空,說明左括號比右括號多,數量不匹配bool ret = STEmpty(&st);STDestroy(&st);return ret;
}

在這里插入圖片描述
提交通過,題目解答完畢。

全文至此結束!!!
寫作不易,不知各位老板能否給個一鍵三連或是一個免費的贊呢(▽)(▽),這將是對我最大的肯定與支持!!!謝謝!!!(▽)(▽)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/74336.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/74336.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/74336.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

datawhale組隊學習--大語言模型—task4:Transformer架構及詳細配置

第五章 模型架構 在前述章節中已經對預訓練數據的準備流程(第 4 章)進行了介紹。本章主 要討論大語言模型的模型架構選擇,主要圍繞 Transformer 模型(第 5.1 節)、詳細 配置(第 5.2 節)、主流架…

BP神經網絡+NSGAII算法(保真)

BP神經網絡NSGAII算法 非常適合用來當作實驗驗證自己的結論,構建一個神經網絡模型,并使用NSGAII多目標優化算法來實現多領域的畢業論文的設計。僅僅使用簡單的matlab代碼就可以實現自己的多目標優化任務。 BP神經網絡算法 我的任務是預測三個變量的值…

MCU vs SoC

MCU(Microcontroller Unit,單片機)和SoC(System on Chip,片上系統)是兩種不同的芯片類型,盡管它們都實現了高度集成,但在設計目標、功能復雜性和應用場景上存在顯著差異。以下是兩者…

3.23學習總結

字符串 String java.lang,String 類代表字符串,Java程序中所有的字符串文字都為此類的對象 字符串的內容是不會發生改變的,它的對象在創建之后不能唄更改 字符串的內存模型 當使用雙引號直接賦值時,系統會檢查該字符串在串池中是否存在。 …

01測試分類

一、按照測試目標分類 1、界面測試 肉眼所看到的一切,都需要進行測試。如,按鈕的點擊;輸入框輸入文本;下拉框的選擇;其它的交互等。。。 前端開發在執行開發之前需要交互/設計的同學給出設計圖(以圖片的…

【Git】用Git命令克隆一個遠程倉庫、修改倉庫中的文件,并將更改推送到遠程倉庫

git clone ssh://gitgithub.com:2222/Mermaid28/Groove.git # SSH地址cd rfnvtoolecho "# rfnvtool" > README.md git add README.mdgit commit -m "add README" git push -u origin master 這個一系列的 Git 命令涉及到克隆一個遠程倉庫、修改倉庫中…

關于MTU的使用(TCP/IP網絡下載慢可能與此有關)

參考鏈接:告訴你mtu值怎么設置才能網速最好! -Win7系統之家 出現網絡速度被限制,可能與MTU值相關,先查看下本機的MTU winR,然后輸入:netsh interface ipv4 show subinterfaces ,查看自己網絡中的MTU&…

07_GRU模型

GRU模型 雙向GRU筆記:https://blog.csdn.net/weixin_44579176/article/details/146459952 概念 GRU(Gated Recurrent Unit)也稱為門控循環單元,是一種改進版的RNN。與LSTM一樣能夠有效捕捉長序列之間的語義關聯,通過引入兩個&qu…

Playwright + MCP:用AI對話重新定義瀏覽器自動化,效率提升300%!

一、引言:自動化測試的“瓶頸”與MCP的革新 傳統自動化測試依賴開發者手動編寫腳本,不僅耗時且容易因頁面動態變化失效。例如,一個簡單的登錄流程可能需要開發者手動定位元素、處理等待邏輯,甚至反復調試超時問題。而MCP&#xf…

網絡爬蟲-4:jsonpath+實戰

1.jsonpath 2.通過jsonpath實戰 一.Jasonpath核心符號 1)$: 含義:表示 JSON 文檔的根節點。 用法:所有 JSONPath 表達式都以 $ 開頭,表示從根節點開始查詢。 {"store": {"book": [{"title": "Book 1&…

GD32 ARM單片機開發規范檢查清單 GD32嵌入式C代碼檢查清單

GD32 ARM單片機開發規范檢查清單 以下檢查清單基于您的編程規范制定,可用于代碼審查和自檢過程。通過逐項檢查,確保代碼符合項目規范要求。 #mermaid-svg-Ye0FEIS4ZoXDXqaH {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:…

求職招聘網站源碼,找工作招工系統,支持H5和各種小程序

招聘找活招工平臺系統源碼 招聘求職找工作軟件 發布信息積分充值招聘系統,里面帶纖細教程 功能介紹: 招工小程序主要針對工地招工工人找工作,工地可以發布招工信息,工人可以發布找活信息,招工信息可以置頂,置頂需要積分,積分可以通過簽到、分享邀請好友、充值獲取,后…

《Oracle DBA入門實戰:十大高頻問題詳解與避坑指南》

Oracle DBA 入門作業十問十答 本文為 Oracle DBA 入門作業整理,涵蓋工具使用、配置管理及權限控制等核心知識點,適合新手快速上手。 如有疑問或補充,歡迎評論區交流! 1. DBA 常用工具有哪些? Oracle Universal Instal…

解決用戶同時登錄輪詢獲取用戶信息錯亂,使用WebSocket和Server-Sent Events (SSE)

為什么更推薦WebSocket Server-Sent Events (SSE) 是一種服務器向客戶端推送數據的單向通信協議,適合某些場景,在解決用戶同時登錄和實時獲取用戶信息的問題上,WebSocket 是更好的選擇。 1. SSE 的局限性 單向通信 SSE 是單向的&#xff0…

發票查驗/發票驗真如何用Java實現接口調用

一、什么是發票查驗?發票驗真接口? 輸入發票基本信息發票代碼、發票號碼、開票日期、校驗碼后6位、不含稅金額、含稅金額,核驗發票真偽。 該接口也適用于機動車、二手車銷售發票、航空運輸電子客票、鐵路電子客票等。 二、如何用Java實現接口…

html5-qrcode前端打開攝像頭掃描二維碼功能

實現的效果如圖所示,全屏打開并且掃描到二維碼后彈窗提醒,主要就是使用html5-qrcode這個依賴庫,html5-qrcode開源地址:GitHub - mebjas/html5-qrcode: A cross platform HTML5 QR code reader. See end to end implementation at:…

cpp-友元

理解 C 中的友元(Friend) 在 C 語言中,封裝(Encapsulation) 是面向對象編程的重要特性之一。它允許類將數據隱藏在私有(private)或受保護(protected)成員中,…

JavaWeb基礎-HTTP協議、請求協議、響應協議

一. HTTP協議 1. HTTP協議:Hyper Text Transfer Protocol,超文本傳輸協議,規定了瀏覽器和服務器之間數據傳輸的規則 2. HTTP協議特點: ① 基于TCP協議:面向鏈接,安全 ② 基于請求-響應模型的:一…

search_fields與filterset_fields的使用

在Django中,search_fields 和 filterset_fields 可以在視圖類中使用,尤其是在 Django REST Framework (DRF) 中。它們分別用于實現搜索和過濾功能。以下是它們在視圖類中的具體使用方法。 1. search_fields 在視圖類中的使用 search_fields 是 DRF 中 S…

數據建模流程: 概念模型>>邏輯模型>>物理模型

數據建模流程 概念模型 概念模型是一種高層次的數據模型,用于描述系統中的關鍵業務概念及其之間的關系。它主要關注業務需求和數據需求,而不涉及具體的技術實現細節。概念模型通常用于在項目初期幫助業務人員和技術人員達成共識,確保對業務需…