【數據結構和算法】--- 棧

目錄

  • 棧的概念及結構
  • 棧的實現
    • 初始化棧
    • 入棧
    • 出棧
    • 其他一些棧函數
  • 小結
  • 棧相關的題目

棧的概念及結構

棧是一種特殊的線性表。相比于鏈表和順序表,棧只允許在固定的一端進行插入和刪除元素操作進行數據插入和刪除操作的一端稱為棧頂,另一端稱為棧底。棧中的數據元素遵守后進先出LIFO(Last In First Out)的原則。

  • 壓棧:棧的插入操作叫做進棧/壓棧/入棧,入數據在棧頂
  • 出棧:棧的刪除操作叫做出棧。出數據也在棧頂

聯想一下,其實棧就相當于手槍的彈夾,將子彈壓入彈夾的操作就相當于壓棧,打出子彈的操作就相當于出棧,每次先打出的子彈都是我們最后壓入彈夾的子彈,最后一顆子彈就是我們最先壓入的那一顆了,這就是后進先出。棧也如此,結構大致如下:

在這里插入圖片描述

基于這樣的結構,那么如果我們想要拿到棧的某個元素,就必須要先把此元素以上的元素依次出棧,然后才能取出。

棧的實現

兩種方式可以實現棧結構-數組棧,鏈式棧

  1. 鏈式棧
    如果用單鏈表實現:若棧底就指向頭節點,棧頂就指向尾節點。這樣設計入棧很方便,相當于頭插,時間復雜度為O(1);但出棧操作就必須要先遍歷鏈表找到棧頂的前一個,然后再出棧,并修改棧頂,相當于尾刪,時間復雜度達到O(N)于是乎我們一般將棧頂指向頭節點,棧底指向尾節點,這樣入棧出棧就都是O(1)了,即頭插/頭刪。

在這里插入圖片描述

如果用雙向鏈表實現:棧頂為鏈表的頭和尾都可以,入棧和出棧時間復雜度都為O(1),但雙向鏈表結構較為復雜,一般不選用此結構

  1. 數組棧
    數組棧的入棧和出棧的實現較為簡單,且時間復雜度為O(1)

在這里插入圖片描述

相較于鏈式棧,數組棧訪問數據時cpu緩存命中率比較高但鏈式棧相較于數組棧也會節省一定的空間。下面棧的實現主要用的是數組棧。
通常我們標識棧頂位置的下一個位置為top(即下標為size的位置)。與標識棧頂位置為top相比較,這樣可以減少棧為空,棧容量判斷等函數的難度,且若標識棧頂位置為top,當棧里面沒有元素時top的指向也較為尷尬。
我們可以如下定義棧結構:

typedef int STDataType;
//數組棧
typedef struct stack
{STDataType* a;int top;//標識棧頂下一個元素下標(同為棧元素個數)int capacity;
}ST;

初始化棧

通過上面對棧的介紹進行初始化。

//初始化
void StackInit(ST* pst)
{assert(pst);pst->top = 0;pst->capacity = 0;pst->a = NULL;
}

入棧

入棧操作就是向數組內增加一個數,首先要判斷棧(數組)容量pst->capacity是否需要增容,然后top位置(即pst->a[top])增加一個數,最后重新變換top指向(即pst->top++),具體如下:

//入棧
void StackPush(ST* pst, STDataType x)
{assert(pst);//判斷增容if (pst->top == pst->capacity){int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDataType* newnode = (STDataType*)realloc(pst->a, sizeof(ST) * newcapacity);if (newnode == NULL){perror("check_ST_capacity()::malloc");return;}pst->a = newnode;pst->capacity = newcapacity;}//目標數x入棧pst->a[pst->top] = x;//變換top指向pst->top++;
}

出棧

出棧操作就相對簡單了,直接改變top指向就可以了(即pst->top--)。如果棧里面已經沒有元素了,那執行此操作top指向就會錯誤,于是乎我們需要斷言一下來確保棧里面有元素可以刪除(即assert(ps->top != 0);)。

//出棧
void StackPop(ST* pst)
{assert(pst);assert(pst->top != 0);pst->top--;
}

其他一些棧函數

  1. 獲得棧頂元素:
    pst->top指向的是棧頂的下一個元素的下標,那么只需要讓他--即可(即pst->a[pst->top-1]),在使用前確保棧中有元素,不然程序會崩潰(越界訪問)
// 獲取棧頂元素 
STDataType StackTop(ST* pst)
{assert(pst);assert(pst->top != 0);return pst->a[pst->top - 1];
}
  1. 獲得棧有效元素個數:
    pst->top指向的既是指向棧頂下一個元素的下標也是整個棧里面有效數據的個數,所以此函數返回pet->top即可。
// 獲取棧中有效元素個數
int StackSize(ST* pst)
{assert(pst);return pst->top;
}
  1. 檢查棧是否為空:
    同理只要棧里面有效元素個數為0,那么棧就是空棧,如下:
// 檢測棧是否為空,如果為空返回非零結果,如果不為空返回0 
bool StackEmpty(ST* pst)
{assert(pst);return pst->top == 0;
}
  1. 棧的銷毀:
    棧的銷毀本質上是釋放先前realloc()開辟的數組,再將容量和棧頂置0即可。
// 銷毀棧 
void StackDestroy(ST* pst)
{assert(pst);assert(pst->capacity != 0);free(pst->a);pst->a = NULL;pst->top = pst->capacity = 0;
}

小結

  • 棧是一種后進先出的結構,這一點恰與我們后面要講的隊列相反;

  • 順序表和鏈表都可以用來實現棧,不過一般都使用順序表,因為棧想當于是閹割版的順序表,只用到了順序表的尾插和尾刪操作,順序表的尾插和尾刪不需要搬移元素,因此效率非常高O(1),故一般都是使用順序表實現;

  • 棧結構中的top一般為要插入位置的下標(即棧頂元素下一個位置),這是為了方便區分棧為空棧的情況,且后續函數更好實現;

  • 棧只能在棧頂進行輸入的插入和刪除操作,不支持隨機訪問;


棧相關的題目

  1. 關于入棧和出棧順序,如下:

若進棧序列為 1,2,3,4 ,進棧過程中可以出棧,則下列不可能的一個出棧序列是()
A 1,4,3,2
B 2,3,4,1
C 3,1,4,2
D 3,4,2,1

不難看出是c選項錯了,因為如果第一個出棧的是3,那么在3之前壓棧的12就都還沒有出棧,所以接下來出棧的只能有兩種情況:

  • 1.4接著入棧然后出棧,即為D選項;
  • 2.直接出先前壓棧的2

對于C選項,此時的1還在棧底,在它上面還有2,所以不能直接出1

  1. LeetCode OJ題: 有效的括號

題目描述:給定一個只包括 '('')''{''}''['']'的字符串s ,判斷字符串是否有效。
有效字符串需滿足:

  • 左括號必須用相同類型的右括號閉合。
  • 左括號必須以正確的順序閉合。
  • 每個右括號都有一個對應的相同類型的左括號。

這題主要考察我們對棧特性的應用,即后進先出,那么我們便可這樣設計:循環遍歷字符串s中的每個字符,滿足以下條件的對棧進行入/出棧操作:

  1. 遇到左括號,直接入棧
  2. 遇到右括號,取棧頂元素進行匹配,若不匹配直接返回false,若匹配就將此括號出棧,并繼續循環。

另外我們還要對如下兩種情況做出判斷

  1. 當遍歷到右括號時,此時棧中是否還有元素?(QueueEmpty()?)為空直接返回false
  2. 當字符串s遍歷結束時,棧中是否還有剩余元素?(QueueEmpty()?)不為空直接返回false,為空返回true

其中一些棧的接口函數就不展示了,上面內容都有,代碼實現如下:

bool isValid(char* s)
{ST st;//創建棧StackInit(&st);//初始化棧//遍歷字符串swhile(*s){if(*s == '(' || *s == '[' || *s == '{'){StackPush(&st,*s);}else{//棧為空判斷,為空返回false,如上講解1處if(StackEmpty(&st)){StackDestroy(&st);return false;}char ch = StackTop(&st);//左右括號匹配判斷,匹配錯誤返回falseif((*s == ')' && ch != '(') || (*s == ']' && ch != '[') ||(*s == '}' && ch != '{')){StackDestroy(&st);return false;}StackPop(&st);}s++;}//棧為空判斷,不為空返回false,與上面判斷處區分,如上講解2處if(!StackEmpty(&st)){StackDestroy(&st);return false;}return true;
}

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

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

相關文章

概率論之 證明 正態分布的上a 分位點的對稱的性質

公式(Z(a) -Z(1-a)) 表示正態分布的上(a)分位點與下(1-a)分位點在分布曲線上關于均值的對稱性。 左側 (Z(a)): 這是分布曲線上累積概率為(a)的那個點。也就是說,這是一個使得這個點及其左側的面積占據整個曲線下方(a)的位置。 右側 (Z(1-a))&#xff1…

Kubernetes(K8s 1.27.x) 快速上手+實踐,無廢話純享版

文章目錄 1 基礎知識1.1 K8s 有用么?1.2 K8s 是什么?1.3 k8s 部署方式1.4 k8s 環境解析 2 環境部署2.1 基礎環境配置2.2 容器環境操作2.3 cri環境操作2.4 harbor倉庫操作2.5 k8s集群初始化2.6 k8s環境收尾操作 3 應用部署3.1 應用管理解讀3.2 應用部署實…

[Firefly-RK3399] TFTP/NFS網絡啟動內核與Buildroot文件系統

?網絡啟動,是用 TFTP 在服務器下載內核、dtb 文件到目標機的內存中,同時可以用 NFS 掛載網絡根文件系統到目標機上,實現目標機的無盤啟動。 準備工作: Firefly-RK3399 板卡;路由器、網線;安裝有 NFS 和 …

微前端 前置知識2--- monorepo架構

目錄 前言 pnpm vs npm pnpm設計思想 硬連接 軟鏈接 (符號鏈接) 原理 pnpm 指令 monorepo架構 介紹 配置monorepo pnpm --filter 前言 我們采用的是微前端一個主應用,和多個子應用,我們肯定不會一個一個去install安裝…

uniapp微信小程序富文本、小程序富文本、rich-text解決video問題

我直接使用的 mp-html mp-html 相當好用,功能比較完善,也可以二次開發 具體的直接看官方文檔吧

Linux安全學習路標

1. 操作系統基礎知識 首先,你需要建立堅實的操作系統基礎知識,包括Linux文件系統和目錄結構、Linux進程管理、權限管理等基本概念。 2. 網絡和通信安全 學習關于網絡和通信安全的基礎知識,包括TCP/IP協議棧、網絡攻擊類型、防火墻配置、網…

vscode + Linux 如何在編輯器調試webserver這類完整C++項目

1. 問題背景 網上搜的一堆文章都是教如何調試單個文件,或者一個文件夾下含了所有cc和頭文件,但很多項目頭文件和實現在上級目錄的子文件中,vscode直接調試main函數所在文件時,直接報錯某些頭文件找不到(xxx.h not found 或者 und…

12.5單端口RAM,JS計數器,流水線乘法器,不重疊序列檢測器(狀態機+移位寄存器),信號發生器,交通燈

單端口RAM timescale 1ns/1nsmodule RAM_1port(input clk,input rst,input enb,input [6:0]addr,input [3:0]w_data,output wire [3:0]r_data );reg [6:0]mem[127:0];integer i;always (posedge clk or negedge rst) beginif(!rst) beginfor (i0; i<127 ; ii1) beginmem[i]…

Linux--權限問題(1)

前文 Linux--初識和基本的指令&#xff08;1&#xff09;-CSDN博客 Linux--初識和基本的指令&#xff08;2&#xff09;-CSDN博客 Linux--初識和基本的指令&#xff08;3&#xff09;-CSDN博客 目錄 前文 前言 1.剩余指令部分 1.1 打包和壓縮的其它指令 2.權限部分 2.1權…

探秘MSSQL存儲過程:參數傳遞、錯誤處理、性能優化

參數傳遞、錯誤處理和性能優化是存儲過程中非常重要的方面。在本節中&#xff0c;我們將深入探討這些主題&#xff0c;并提供相應的示例代碼。 1、參數傳遞 存儲過程可以接受輸入參數和輸出參數&#xff0c;以便與外部代碼進行交互。以下是一些常見的參數傳遞方式&#xff1a;…

Qt基礎-程序打包發布方法

本文講解Qt程序打包發布方法。 一、使用Qt自帶的windeployqt 生成可運行的包 準備將Qt生成的exe拷入到單獨的文件夾,并進行命名,本文命名為packDemorun,并將文件放到D盤(自己隨意放置) 1、找到Qt自帶的命令終端 2、啟動命令終端 3、輸入:cd /d D:\packDemorun,進入文…

IDEA刪除最近打開的文件記錄

IDEA刪除最近打開的文件記錄 遇見問題&#xff1a;如何刪除IDEA中最近打開的文件記錄 解決方法 先關閉IDEA 找到 recentProjects.xml 文件 windows 位置&#xff1a;&#xff08;AppData是隱藏文件夾&#xff09; 1.C:\Users\電腦用戶名\AppData\Roaming\JetBrains\IntelliJIde…

Git 請輸入一個提交信息以解釋此合并的必要性

操作方法&#xff1a;按住Ctrl加下面的某個字母

linux-man命令的使用及練習

目錄 1. 命令概述 2. 使用 3. 練習 ?man services時報錯&#xff1a;No manual entry for services的解決辦法 4. man命令中常用按鍵以及用途 1. 命令概述 Linux提供了豐富的幫助手冊&#xff0c;當你需要查看某個命令的參數時不必到處上網查找&#xff0c;只要man一下即…

MySQL六 | 索引

目錄 索引 優缺點 結構 語法 創建索引 查看索引 刪除索引 索引 索引是幫助數據庫高效獲取數據的數據結構。如果沒有設置索引會進行全表掃描&#xff0c;性能較低。 優缺點 優點缺點提高數據檢索的效率&#xff0c;降低數據的IO成本索引列也是要占用空間的通過索引列對數…

viewPager的adapter--FragmentInstancePagerAdapter

之前分享過幾個tabviewPager的庫。。這種東西開發中特別常見。今天抽空補一個viewPager的adapter。用來搭配使用 創建FragmentInstancePagerAdapter,如下&#xff1a; mport androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.f…

AI降重軟件,AI降重后原創高質量文章

在當今信息爆炸的時代&#xff0c;寫作與創作的重要性日益凸顯。隨著大量內容的涌現&#xff0c;文章降重成為了許多作者和內容創作者的一大問題。本文將專心分享該軟件的優勢&#xff0c;并為廣大用戶推薦幾款好用的AI降重軟件。 AI降重使用場景 AI降重技術利用機器學習算法和…

OpenCV圖像相似性比對算法

背景 在做圖像處理或者計算機視覺相關的項目的時候&#xff0c;很多時候需要我們對當前獲得的圖像和上一次的圖像做相似性比對&#xff0c;從而找出當前圖像針對上一次的圖像的差異性和變化點&#xff0c;這需要用到OpenCV中的一些圖像相似性和差異性的比對算法&#xff0c;在O…

使用LangSmith來快速學習LangChain

好風憑借力&#xff0c;送我上青云&#xff01; 什么是LangSmith LangSmith is a platform for building production-grade LLM applications. It lets you debug, test, evaluate, and monitor chains and intelligent agents built on any LLM framework and seamlessly int…

Python學習路線 - Python語言基礎入門 - 循環語句

Python學習路線 - Python語言基礎入門 - 循環語句 前言為什么學習循環語句 while循環的基礎語法while循環語句while循環注意點 while循環的基礎案例while循環的嵌套應用while循環的嵌套 while循環的嵌套案例補充知識 - print輸出不換行補充知識 - 制表符\t練習案例 - 打印九九乘…