棧和隊列特別篇:棧和隊列的經典算法問題

圖均為手繪,代碼基于vs2022實現

系列文章目錄

數據結構初探: 順序表
數據結構初探:鏈表之單鏈表篇
數據結構初探:鏈表之雙向鏈表篇
鏈表特別篇:鏈表經典算法問題
數據結構:棧篇
數據結構:隊列篇


文章目錄

  • 系列文章目錄
  • 前言
  • 一.有效的括號(leetcode 20)
  • 二.用隊列實現棧(leetcode 225)
  • 三.用棧實現隊列(leetcode 232)
  • 四.設計循環隊列(leetcode 622)
  • 五.總結


前言

  • 棧和隊列作為基礎數據結構,是算法設計中的重要基石。它們在操作系統、編譯器設計、網絡協議等領域有著廣泛應用。本文將通過C語言實現經典算法問題,幫助讀者深入理解它們的底層原理和應用技巧。學習這些內容不僅能提升算法能力,還能加深對內存管理和數據結構設計的理解。

一.有效的括號(leetcode 20)

讓我們先來看看題目:
在這里插入圖片描述
讓我們來理清楚思路:

  • 首先,我們可以用棧來實現匹配

1.我們將左括號入棧;
2.在遍歷中找到右括號,進行匹配;

  • 在這個過程中,我們需要手撕一個棧的代碼出來,加在題目代碼之前,如果發現還不太熟練,可以再去我的這篇博客中熟悉熟悉—> 數據結構:棧篇
  • 我們還是上代碼實際感受一下吧(考慮到篇幅有限,已省略棧的代碼):
//注意這里是字符,棧的代碼中要將int改為char
//typedef char STDataType;
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;//直接返回false,表示找不到匹配的括號}//因為題目要求的是按順序一一對應;char top=STTop(&st);//此時記錄下棧頂元素STPop(&st);//再pop,表示出棧操作//匹配括號邏輯結構//大家可以自行分析;//即如果右括號沒有在棧頂找到對應的左括號,則錯誤if((*s == ')' && top != '(')||(*s == ']' && top != '[')||(*s == '}' && top != '{')){STDestroy(&st);//所以銷毀,防止內存泄漏return false;//直接返回false,表示找不到匹配的括號}}s++;//更新循環條件}bool ret=STEmpty(&st);//記錄是否找到對應括號的狀態STDestroy(&st);//銷毀,防止內存泄漏return ret;//返回對應狀態
}

在這里插入圖片描述

學完后,自己多多練習,自行手撕,理清邏輯即可;

二.用隊列實現棧(leetcode 225)

讓我們先來看看題目:
在這里插入圖片描述
讓我們來理清楚思路:
我們需要用到兩個隊列來實現棧:

  • 一個用來當作入棧隊列
  • 一個用來當作出棧隊列
  • 我們還要注意其LIFO的性質,和隨進隨出的特點
    1.我們需要保持一個隊列為空,一個隊列存數據
    2.出棧,把前面的數據導入空隊列
    3.如此兩班來回倒,就可以實現棧的特性;
    邏輯如圖:
    在這里插入圖片描述
    在這個過程中,我們需要手撕隊列的代碼出來,加在題目代碼之前,如果發現還不太熟練,可以再去我的這篇博客中熟悉熟悉—> 數據結構:隊列篇
    我們來實戰一下:
//創建兩個隊列的結構體
typedef struct {Queue q1;Queue q2;
} MyStack;
//我們需要動態開辟出空間來實現mystack
MyStack* myStackCreate() {MyStack* pst=(MyStack*)malloc(sizeof(MyStack));if(pst == NULL){perror("malloc fail");return NULL;}QueueInit(&pst->q1);//對兩個隊列初始化QueueInit(&pst->q2);return pst;//返回初始化后的我們的mystack
}
//來實現棧的插入
void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(&obj->q1))//如果q1不為空就插入到q1里{//反正有一個隊列里面是空的,我們不能插入,一旦插入就會弄亂整體的邏輯QueuePush(&obj->q1,x);//可以參看上文圖進行理解}else//如果q2不為空就插入到q2里{//兩個都為空就隨便選一個QueuePush(&obj->q2,x);}
}
//實現棧的刪除,題目要求:移除并返回棧頂元素
int myStackPop(MyStack* obj) {//首先要明確哪個為空,哪個為非空Queue* emptyQ=&obj->q1;//假設q1為空Queue* nonemptyQ=&obj->q2;//q2不為空if(!QueueEmpty(&obj->q1))//驗證是否正確{//進入則是不正確emptyQ=&obj->q2;//不正確則交換nonemptyQ=&obj->q1;}while(QueueSize(nonemptyQ) > 1)//設置遍歷循環{QueuePush(emptyQ,QueueFront(nonemptyQ));//將非空的前面n-1個挪入空隊列QueuePop(nonemptyQ);//pop掉已經挪入的那n-1個}//剩下的就是要按照棧的特性LIFO順序的數據int top=QueueFront(nonemptyQ);//按照題目要求記錄下數據QueuePop(nonemptyQ);//popreturn top;//返回數據
}
//返回棧頂元素
int myStackTop(MyStack* obj) {if(!QueueEmpty(&obj->q1))//找非空隊列{return QueueBack(&obj->q1);//返回隊尾元素,按照模擬邏輯}//此時隊尾就是棧頂else{return QueueBack(&obj->q2);}
}
//判空
bool myStackEmpty(MyStack* obj) {
//兩個隊列都為空,模擬棧才為空return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
//銷毀
void myStackFree(MyStack* obj) {QueueDestroy(&obj->q1);//將自己開辟的空間都釋放QueueDestroy(&obj->q2);//否則會有內存泄漏問題free(obj);
}

銷毀邏輯示例圖:
在這里插入圖片描述

在這里插入圖片描述

三.用棧實現隊列(leetcode 232)

讓我們先來看看題目:
在這里插入圖片描述
讓我們來理清楚思路:
我們需要用到兩個棧來實現隊列:

  • 一個用來當作入隊棧
  • 一個用來當作出隊棧
  • 我們還要注意其FIFO的性質,和隨進隨出的特點
    1.我們需要保持一個入隊棧,一個出隊棧
    2.出隊列,把前面的數據導入空棧
    3.當出隊棧不為空時,不能再將出隊棧中的數據導入,否則順序會亂;
    如圖:
    在這里插入圖片描述
    讓我們來感受一下代碼:
typedef struct {ST pushst;//入隊棧ST popst;//出隊棧
} MyQueue;int myQueuePeek(MyQueue* obj);//返回隊頭元素的函數聲明;
//動態開辟
MyQueue* myQueueCreate() {MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));if(obj==NULL){perror("malloc fail");return NULL;}STInit(&obj->pushst);//初始化兩個棧STInit(&obj->popst);return obj;//返回模擬隊列
}void myQueuePush(MyQueue* obj, int x) {STPush(&obj->pushst,x);//正常往入隊棧中直接插入
}
//按題目要求,從隊列的開頭移除并返回元素
int myQueuePop(MyQueue* obj) {int front=myQueuePeek(obj);//記錄隊頭元素STPop(&obj->popst);//刪除return front; //返回元素
}int myQueuePeek(MyQueue* obj) {if(STEmpty(&obj->popst))//如果出隊棧為空{while(!STEmpty(&obj->pushst))//開始遍歷循環{//并且入隊棧不為空STPush(&obj->popst,STTop(&obj->pushst));//將全部元素挪過去STPop(&obj->pushst);//在入隊棧中刪除的操作}}return STTop(&obj->popst);//返回棧頂即是隊頭,可以參看上圖;
}
//判空
bool myQueueEmpty(MyQueue* obj) {
//兩個棧都為空,才為空return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}
//釋放所有動態開辟的空間
void myQueueFree(MyQueue* obj) {STDestroy(&obj->pushst);STDestroy(&obj->popst);free(obj);}

在這里插入圖片描述

四.設計循環隊列(leetcode 622)

讓我們來看看題目:
在這里插入圖片描述
我們來理清楚邏輯:
這里我們選擇用數組來實現,鏈表實現也是可以的,但是相對來說,數組實現會更加容易;對于數組是否滿了,我們有兩種解決辦法:

  • 在結構體中加入size來計量;
  • 空出一個位置來提醒判斷已滿
    讓我們來實戰:
//定義結構體
typedef struct {int* a;//靜態數組int front;//頭int rear;//尾int k;//滿數據時候的個數
} MyCircularQueue;
bool myCircularQueueIsEmpty(MyCircularQueue* obj);//函數聲明,防止編譯錯誤
bool myCircularQueueIsFull(MyCircularQueue* obj);
//開出對應大小的空間
MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));if(obj==NULL){perror("malloc fail");return NULL;}obj->a=(int*)malloc(sizeof(int)*(k+1));//多開,防止溢出if(obj->a==NULL){perror("malloc fail");return NULL;}obj->front=obj->rear=0;//指向開頭obj->k=k;return obj;
}
//插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj))//如果是滿的{return false;//返回false,表示不能再插入了}obj->a[obj->rear++] = value;//否則存儲值,并且rear++到下一個位置;obj->rear %= (obj->k+1);//對尾取模,比如,1%5==1,3%5==3,如果滿了5%5==0//就會循環回到數組開始的下標return true;//返回true,表示還可以插入
}
//刪除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))//如果是空的{return false;//返回false,表示不能再刪除了}obj->front++;//頭往下一個位置走,這個就與我們曾經講過的順序表里面的刪除一樣的//只要我們不把它計量在有效個數中就可以刪除它obj->front %= (obj->k+1);//對頭取模,比如,1%5==1,3%5==3,如果滿了5%5==0//就會循環回到數組開始的下標return true;//返回true,表示還可以刪除
}
//按題目要求:從隊首獲取元素。如果隊列為空,返回 -1
int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))//若為空{return -1;//返回}return obj->a[obj->front];//返回頭元素
}
//按題目要求:獲取隊尾元素。如果隊列為空,返回 -1 
int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;}return obj->a[(obj->rear-1+obj->k+1) % (obj->k+1)];//防止因為rear在下標0的位置--,而發生錯誤;//通過取模,來造成循環;
}
//簡單的判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->rear == obj->front;
}
//判斷滿
bool myCircularQueueIsFull(MyCircularQueue* obj) {//通過取模,來造成循環;return (obj->rear+1) % (obj->k+1) == obj->front;//保證rear的下一個是頭
}
//釋放,相同與前兩題,可以自行畫圖理解;
void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}

以上就是我要講的所有的經典問題,你學會了嗎?


五.總結

在本次博客中,我們圍繞棧和隊列這兩種基礎數據結構,通過C語言實現經典算法問題,深入探索了它們的底層原理與應用技巧。

  • 首先是“有效的括號”問題,利用棧來匹配括號,左括號入棧,右括號與棧頂元素匹配,這種方式充分展現了棧“后進先出”(LIFO)的特性,在處理具有成對結構的數據時十分有效 ,能夠清晰地判斷括號是否匹配,避免邏輯混亂。
  • 接著,在“用隊列實現棧”和“用棧實現隊列”的問題中,分別運用兩個隊列和兩個棧來模擬對方的特性。用隊列實現棧時,通過在兩個隊列間來回轉移數據,確保滿足棧的LIFO性質;用棧實現隊列則是利用兩個棧,在入隊棧和出隊棧間合理轉移數據,保證隊列“先進先出”(FIFO)的特性。這兩個問題不僅鍛煉了對數據結構特性的靈活運用,還讓我們深入理解了不同數據結構間的轉換和模擬方法。
  • 最后,在“設計循環隊列”中,使用數組實現循環隊列,通過巧妙的取模操作實現隊列的循環,同時提供了判空和判滿的方法。這種實現方式既高效又簡潔,充分利用了數組的連續性和可索引性,加深了我們對隊列數據結構的理解和應用能力。

通過解決這些經典算法問題,我們不僅掌握了棧和隊列在實際編程中的應用,還提升了算法設計、邏輯思維以及內存管理的能力。希望讀者能夠通過不斷練習,熟練掌握這些知識,在后續的編程學習和實踐中更加得心應手,靈活運用棧和隊列解決各種復雜的實際問題,為更深入的算法學習和系統開發打下堅實的基礎。

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

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

相關文章

ios swift畫中畫技術嘗試

繼上篇:iOS swift 后臺運行應用嘗試失敗-CSDN博客 為什么想到畫中畫,起初是看到后臺模式里有一個picture in picture,去了解了后發現這個就是小窗口視頻播放,方便用戶執行多任務。看小窗口視頻的同時,可以作其他的事情…

OpenAI推出o3-mini推理模型,首次免費開放,性能超越o1,AIME測試準確率高達87.3%

OpenAI在2025年初推出了一款新的推理模型o3-mini,這款模型標志著公司在提升性能的同時也降低了成本,并且首次向免費用戶提供訪問權限。o3-mini是OpenAI推理系列中最新、最具成本效益的模型,在科學、數學、編程等領域的性能顯著超越了之前的o1…

人生不止于職業發展

0 你的問題,我知道! 工作意義是啥?職業發展在人生啥角色? 1 工作意義 農村人努力學習考上大學,得好工作,為逃離同村同齡人十幾歲就工廠打工命運,過不凡人生,實現改命的唯一途徑。…

【算法設計與分析】實驗3:動態規劃—最長公共子序列

目錄 一、實驗目的 二、實驗環境 三、實驗內容 四、核心代碼 五、記錄與處理 六、思考與總結 七、完整報告和成果文件提取鏈接 一、實驗目的 掌握動態規劃求解問題的思想;針對不同的問題,會利用動態規劃進行設計求解以及時間復雜度分析&#xff0…

動手學圖神經網絡(3):利用圖神經網絡進行節點分類 從理論到實踐

利用圖神經網絡進行節點分類:從理論到實踐 前言 在之前的學習中,大家對圖神經網絡有了初步的了解。本次教程將深入探討如何運用圖神經網絡(GNNs)來解決節點分類問題。在節點分類任務里,大家往往僅掌握少量節點的真實標簽,卻要推斷出其余所有節點的標簽,這屬于歸納式學…

單片機串口打印printf函數顯示內容(固件庫開發)

1.hal_usart.c 文件 #include <stdio.h> #include "hal_usart.h" #include "stm32F10x.h"//**要根據 使用的是哪個串口 對應修改 串口號 eg&#xff1a;USART1** void USART_PUTC(char ch) {/* 等待數據寄存器為空 */while((USART1->SR & …

網關登錄校驗

網關登錄校驗 單體架構時我們只需要完成一次用戶登錄、身份校驗&#xff0c;就可以在所有業務中獲取到用戶信息。而微服務拆分后&#xff0c;每個微服務都獨立部署&#xff0c;不再共享數據。也就意味著每個微服務都需要做登錄校驗&#xff0c;這顯然不可取。 鑒權思路分析 …

wxwidgets直接獲取系統圖標,效果類似QFileIconProvider

目前只做了windows版本&#xff0c;用法類似QFileIconProvider // 頭文件 #ifndef WXFILEICONPROVIDER_H #define WXFILEICONPROVIDER_H#include <wx/wx.h> #include <wx/icon.h> #include <wx/image.h> #include <wx/bmpcbox.h> // Include for wxB…

我的創作紀念日——成為創作者的 第365天(1年)

機緣 考研的結果讓我感到一陣絕望&#xff0c;就像單片機突然死機一樣&#xff0c;所有的努力像是被一場意外的中斷指令打亂了邏輯流程。曾經本科時因為競賽拿了一堆獎&#xff0c;內心充滿虛榮心和成就感&#xff0c;總覺得自己是一個“天選之子”&#xff0c;但考研的失利卻像…

React 封裝高階組件 做路由權限控制

React 高階組件是什么 官方解釋∶ 高階組件&#xff08;HOC&#xff09;是 React 中用于復用組件邏輯的一種高級技巧。HOC 自身不是 React API 的一部分&#xff0c;它是一種基于 React 的組合特性而形成的設計模式。 高階組件&#xff08;HOC&#xff09;就是一個函數&…

【玩轉全棧】--創建一個自己的vue項目

目錄 vue介紹 創建vue項目 vue頁面介紹 element-plus組件庫 啟動項目 vue介紹 Vue.js 是一款輕量級、易于上手的前端 JavaScript 框架&#xff0c;旨在簡化用戶界面的開發。它采用了響應式數據綁定和組件化的設計理念&#xff0c;使得開發者可以通過聲明式的方式輕松管理數據和…

DS并查集(17)

文章目錄 前言一、何為并查集&#xff1f;二、并查集的實現&#xff1f;并查集的初始化查找元素所在的集合判斷兩個元素是否在同一個集合合并兩個元素所在的集合獲取并查集中集合的個數并查集的路徑壓縮 三、來兩道題練練手&#xff1f;省份的數量等式方程的可滿足性 總結 前言…

Appium介紹

在使用不同版本的Appium包進行自動化測試時&#xff0c;出現警告問題可能是由于版本不兼容、配置不正確等原因導致的。下面將詳細介紹解決這些問題的步驟&#xff0c;確保模擬器能夠正常啟動&#xff0c;并能在Appium查看器中同步顯示。 1. 環境準備 首先&#xff0c;確保你已…

minimind - 從零開始訓練小型語言模型

大語言模型&#xff08;LLM&#xff09;領域&#xff0c;如 GPT、LLaMA、GLM 等&#xff0c;雖然它們效果驚艷&#xff0c; 但動輒10 Bilion龐大的模型參數個人設備顯存遠不夠訓練&#xff0c;甚至推理困難。 幾乎所有人都不會只滿足于用Lora等方案fine-tuing大模型學會一些新的…

【C++動態規劃 離散化】1626. 無矛盾的最佳球隊|2027

本文涉及知識點 C動態規劃 離散化 LeetCode1626. 無矛盾的最佳球隊 假設你是球隊的經理。對于即將到來的錦標賽&#xff0c;你想組合一支總體得分最高的球隊。球隊的得分是球隊中所有球員的分數 總和 。 然而&#xff0c;球隊中的矛盾會限制球員的發揮&#xff0c;所以必須選…

CSS 值和單位詳解:從基礎到實戰

CSS 值和單位詳解&#xff1a;從基礎到實戰 1. 什么是 CSS 的值&#xff1f;示例代碼&#xff1a;使用顏色關鍵字和 RGB 函數 2. 數字、長度和百分比2.1 長度單位絕對長度單位相對長度單位 2.2 百分比 3. 顏色3.1 顏色關鍵字3.2 十六進制 RGB 值3.3 RGB 和 RGBA 值3.4 HSL 和 H…

Privacy Eraser,電腦隱私的終極清除者

Privacy Eraser 是一款專為保護用戶隱私而設計的全能型軟件&#xff0c;它不僅能夠深度清理計算機中的各類隱私數據&#xff0c;還提供了多種系統優化工具&#xff0c;幫助用戶提升設備的整體性能。通過這款軟件&#xff0c;用戶可以輕松清除瀏覽器歷史記錄、緩存文件、Cookie、…

Android 啟動流程

一 Bootloader 階段 在嵌入式系統中&#xff0c;Bootloader的引導過程與傳統的PC環境有所不同&#xff0c;主要是因為嵌入式系統的硬件配置和應用場景更加多樣化。以下是嵌入式系統中Bootloader被引導的一般流程&#xff1a; 1. 硬件復位 當嵌入式設備上電或復位時&#xff…

【數據結構與算法】AVL樹的插入與刪除實現詳解

文章目錄 前言Ⅰ. AVL樹的定義Ⅱ. AVL樹節點的定義Ⅲ. AVL樹的插入Insert一、節點的插入二、插入的旋轉① 新節點插入較高左子樹的左側&#xff08;左左&#xff09;&#xff1a;右單旋② 新節點插入較高右子樹的右側&#xff08;右右&#xff09;&#xff1a;左單旋③ 新節點插…

SCRM開發為企業提供全面客戶管理解決方案與創新實踐分享

內容概要 在當今的商業環境中&#xff0c;客戶關系管理&#xff08;CRM&#xff09;變得越來越重要。而SCRM&#xff08;社交客戶關系管理&#xff09;作為一種新興的解決方案&#xff0c;正在幫助企業徹底改變與客戶的互動方式。快鯨SCRM是一個引人注目的工具&#xff0c;它通…