Java-數據結構-棧與隊列(常考面試題與單調棧)

在上一篇的學習中,我們學習了棧和隊列的基本知識,以及它們對應都有哪些方法,在什么應用場景下如何使用,并且還對它們進行了模擬實現,而其實對于棧和隊列的相關知識還遠不止于此,而今天我們就對棧與隊列進行復盤,認識更多使用它們的場景,夯實代碼功底吧~

一、常考面試題-思維

以下習題在leetcode都是比較熱門的題,并且大部分都能夠代表一系列的解題方式,推薦大家自己多嘗試幾遍,練熟練透哦~

第一題、最小棧

📚 思路提示

該題要求我們實現一個擁有"能夠直接獲取棧內最小元素方法"的棧,并且要求時間復雜度為O(1)要知道我們之前所學習的棧可是沒有這種高能的方法的~而這是為什么呢?

因為當我們把元素壓入棧中之后想要再對其中的元素進行訪問是做不到的,如果要強行訪問,那么就需要將棧頂元素一個個拿出來,而當找到最小值的那一刻,該棧的元素也就都被掏空了,也就更不用說時間復雜度還達到了O(n)。

當然,做不到是因為只有一個棧,而我們可以采取創建輔助棧的方式,來模擬實現這種"O(1)查找最小元素"的棧具體的實現步驟如下所述

📕 創建一個輔助棧,用于存儲當前"最小棧"中的最小值

📕 只有"入棧元素 <= 最小值"時,才入最小棧( == 時必須入棧,否則可能發生"普通棧有兩個最小值,而最小棧只有一個最小值,最小值出棧后,最小棧中就不是當前最小值了"的情況)

📕 需要注意,"最小棧"中的最小值可能被出棧,所以輔助棧存儲的最小值也要跟隨一起改變

📕 注意出棧操作時,保證兩個棧都不為空

? 圖解

📖 代碼示例

class MinStack {Stack<Integer> stack1;Stack<Integer> stack2;int n = Integer.MAX_VALUE;public MinStack() {stack1 = new Stack<>();stack2 = new Stack<>();}public void push(int node) {if (node < n) {n = node;}stack2.push(n);stack1.push(node);}public void pop() {stack1.pop();stack2.pop();if(!stack2.isEmpty() && stack2.peek() >= n){n = stack2.peek();}if(stack2.isEmpty()){n = Integer.MAX_VALUE;}}public int top() {return stack1.peek();}public int getMin() {return stack2.peek();}
}

第二題、字符串解碼

📚 思路提示

該題要求我們將"數學表達式的字符串"改寫成"展開后的字符串",光看測試用例大家或許覺得簡單,但看似簡單,其實需要注意的細節還是很多的,比如它還有三個測試用例:

這樣看起來就有些讓人抓心撓肝,難受的不行了。

而對于這題的解決方式,我們仍然是采用"輔助棧"的方法而且這次我們需要不止一個,而是需要"兩個輔助棧"。

至于需要用兩個輔助棧,是因為我們不能光存"倍數"或者"字符",因為我們想達到O(n)的時間復雜度,就要求我們遍歷一次成功,但我們可以注意到:

以示例2為參考例子"3 [ a 2 [ c ] ]"? ? ==》 "accaccacc"

想要結果有"cc",就需要將 [ c ] 于它之前的 2 相對應,而我們遍歷到 [ c ] 的時候,已經越過了 2 ,所以如果我們想不存儲數字,是做不到這一點的。

而如果想有"acc",我們就必須再將 a 接在 cc 的前邊,但是當我們合成 cc 后,早就遍歷過了 a ,所以我們也必須用一個棧存儲字符,才能得到最終的字符串。

而具體怎么存呢?我們可以通過一個"res"來存儲臨時的字符串,"multi"來存儲臨時的數字,' [ ' 來作為分段的符號,

📕 每當遇到 ' [ ' ,我們便將此時這一組字符串于數字存入棧中,以便不與其他部分搞混

📕 每當遇到 ' ]?' ,我們便將此時的"臨時字符串"與"棧內數字"進行結合

(因為 ' [ ' 前數字是與 ' [?'?后字符串結合后的因子,因此二者匹配)

? 圖解

📖 代碼示例

class Solution {public static String decodeString(String s) {int multi = 0;StringBuilder res = new StringBuilder();Stack<String> stack1 = new Stack<>();Stack<Integer> stack2 = new Stack<>();char[] str = s.toCharArray();for(int i = 0;i < str.length;i++){char c = str[i];if(c == '['){stack1.push(res.toString());stack2.push(multi);multi = 0;res = new StringBuilder();}else if(c == ']'){StringBuilder ss = new StringBuilder();int n = stack2.pop();for(int j = 0;j < n;j++){ss.append(res);}res = new StringBuilder(stack1.pop() + ss);}else if(Character.isDigit(c)){multi = multi * 10 + (c - 48);}else {res.append(c);}}return res.toString();}
}

第三題、逆波蘭表達式求值

📚 思路提示

此題并不難,只要搞清了逆波蘭表達式如何求,并且逆波蘭表達式如何再還原成原式就好了~

所以我們直接看圖,只要了解了過程,這題也就迎刃而解了~

? 圖解

算數計算式轉逆波蘭表達式

波蘭表達式求表達式的值:

(用后綴表達式求結果)

📕 遇到數字 放入棧內 遇到運算符 彈出棧頂

📕 兩個元素 第一個元素是右操作數 第二個元素是左操作數

📕 然后再將結果放回棧內

📖 代碼示例

class Solution {public int evalRPN(String[] tokens) {Stack stack = new Stack();for(int i = 0;i < tokens.length;i++){String s = tokens[i];if(!doNum(s)){stack.push(Integer.valueOf(s));} else {int a = (int)stack.pop();int b = (int)stack.pop();switch(s){case "+":stack.push(b + a); break;case "-":stack.push(b - a); break;case "*":stack.push(b * a); break;case "/":stack.push(b / a); break;}}}return (int)stack.pop();}public boolean doNum(String s){if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")){return true;}return false;}
}

二、常考面試題-單調棧

"他向遠方望去,無法看到高山背后的矮山,只能看到一座座更高的山峰。"

(引用靈神的話~)

我們上面已經練習了一部分棧的習題,那么關于這里的單調棧又是什么呢?其實和名字一樣

單調棧:要求從棧的一端,到棧的另一端,元素必須是呈單調遞增或單調遞減的~

而單調棧分為兩種,一種是單調遞增棧,一種是單調遞減棧~

📕 單調遞增棧:從棧低到棧頂,元素單調遞增的棧

📕 單調遞減棧:從棧低到棧頂,元素單調遞減的棧

我們可以舉一個例子來看一下,如何通過一組數據來獲得我們的單調棧

? 圖解(該棧為單調遞減棧)

那么單調棧應該運用于哪些場景呢?讓我們做幾道例題試試~

第一題、商品折扣后的最終價格

📚 思路提示

我們分析一下這道題,我們主要需要做的就是"找出當前價格后的第一個小于它的價格"并且找到我們需要的較小價格后,就可以將當前價格折扣后的價格計算出來了,也就是說可以將它舍棄掉了(也就是出棧~)

那么可以將價格數組 prices 進行遍歷,使用一個輔助棧來將未被解決的價格(的下標)存入棧中。

然后繼續遍歷與壓棧直到遇到比目前棧頂價格更小的價格(也就是目標的折扣),就可以算出目前棧頂價格的折扣價格,并將其出棧操作~

(可能目前的 prices[i] 價格也小于棧頂下一個價格,所以此處應用 "while" 而不是 "if")

最后將棧中剩余未被解決的價格,直接原價出售即可~

怎么樣,很明顯,這也是一道單調棧問題吧~其實單調棧的辨識度還是很高的,只要題中要求你找到一個比一個數值更高/低的元素,并且分析后可以省略之前元素。那么它大概率就是一個單調棧問題~

? 圖解

📖 代碼示例

class Solution {public int[] finalPrices(int[] prices) {Deque<Integer> deque = new ArrayDeque<>();int len = prices.length;int[] arr = new int[len];for(int i = 0;i < len;i++){int price = prices[i];while(!deque.isEmpty() && price <= prices[deque.peek()]){int index = deque.pop();arr[index] = prices[index] - prices[i];}deque.push(i);arr[i] = prices[i];}return arr;}
}

第二題、每日溫度

📚 思路提示

這題的主要思路與上一題還是基本一致的~只不過上一題要找的是越來越小的元素(單調遞增棧),而我們這題需要找的是越來越大的元素(單調遞減棧)

一樣的思路,遍歷溫度數組,將未被解決的溫度存入棧中(下標)。

如果找到比棧頂溫度更高的溫度,則計算出棧頂溫度與該溫度相差天數,隨后將已得出結果的棧頂溫度出棧,將目前溫度入棧

最后棧中未被解決的溫度,將它們的天數用0代替。

? 圖解

📖 代碼示例

class Solution {public int[] dailyTemperatures(int[] temperatures) {int len = temperatures.length;int[] arr = new int[len];Deque<Integer> stack = new ArrayDeque<>();stack.push(0);for(int i = 0;i < len;i++){int num = temperatures[i];while(!stack.isEmpty() && num > temperatures[stack.peek()]){int index = stack.pop();arr[index] = i - index;}stack.push(i);}return arr;}
}

第三題、股票價格跨度

📚 思路提示

而題中要求我們將傳入的數據,一個個往前進行比較,如果前一天的價格小于今天的價格,那么"入棧的股票價格跨度" += "棧頂元素的股票價格跨度",并且前一天的前一天也可能小于今天得價格,所以還是while而不是if~(那么這就是一個單調遞減棧)

前兩道題讓我們完成的是一個"方法"而這道題是想讓我們設計實現一個"類"

前兩道題給我們的是一個數組,我們就對數組進行一個遍歷,然后通過每一步遍歷,同時檢查并刪改棧頂元素即可,而這題我們自己輸入元素,而并非給我們一個數組。

而對于這一點不同,我們的解題方法需要有什么修改呢?讓我們思考一下

之前我們的棧內只存儲了一個元素(下標),這是因為我們的數據值在題中所給出的數組中存儲著~而當題目并沒有給我們數組,而是讓我們自己輸入數據時,我們的棧便不能只存儲一個數據了,而是必須將每日的股票價格與跨度同時存儲,所以我們創建的棧應該是<int[]>類型的~

需要注意的是

📕 由于同時存儲了跨度,當對不滿足單調性的數據進行出棧時,我們僅僅是想在后續訪問過程中忽略該結點的股票價格,而忽略并不代表消失,所以即便后續訪問時跳過它,但它的天數仍是做數的!

📕 所以出棧時,我們需要同時對 "入棧的股票價格跨度" += "棧頂元素的股票價格跨度",這不僅僅是因為我們要求出該天的跨度,也是為了后續股票訪問到這里時,也一并算上被省略的天數!

? 圖解

📖 代碼示例

class StockSpanner {Deque<int[]> stack;public StockSpanner() {stack = new ArrayDeque<int[]>();}public int next(int price) {int day = 1;while(!stack.isEmpty() && stack.peek()[1] <= price){day += stack.pop()[0];}stack.push(new int[] {day,price});return day;}
}

那么這次關于棧與隊列(常考面試題)以及(單調棧)的知識,就為大家分享到這里啦,作者能力有限,如果有講得不清晰或者不正確的地方,還請大家在評論區多多指出,我也會虛心學習的!那我們下次再見哦

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

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

相關文章

JSON.stringify(res,null,2)的含義

JSON.stringify(res, null, 2) 是 JavaScript 中將對象轉換為 JSON 字符串的方法&#xff0c;具體說明如下&#xff1a; 參數解釋 res&#xff1a;要轉換的對象。它可以是 JavaScript 中的任意類型&#xff0c;如對象、數組、字符串、數字等。例如&#xff0c;{name: "K…

Spring 項目 基于 Tomcat容器進行部署

文章目錄 一、前置知識二、本地Idea運行Spring項目1. 將寫好的 Spring 項目先打包成 war 包2. 查看項目工件&#xff08;Artifact&#xff09;是否存在3. 配置 Tomcat3.1 添加一個本地 Tomcat 容器3.2 將項目部署到 Tomcat 4. 運行項目 三、基于 Tomcat 部署及多實例部署1. Spr…

usbredir學習

文章目錄 背景典型場景編譯usbredirparserusbredirfilterusbredirparser/usbredirproto usbredirhostusbredirect/usbredirtestclient參考 背景 usbredir 是一種用于通過網絡轉發 USB 設備流量的網絡協議。它也是一個軟件包的名稱&#xff0c;該軟件包提供了一個解析庫、一個 …

ESXI 安裝教程(3) ---?vCenter Server 安裝

不涉及復雜的操作此項可不安裝 1.鏡像加載到虛擬光盤 對應的網盤文件 2.打開文件路徑 雙擊運行文件installer.exe 3.調整安裝語言 4.點擊安裝 5. 6. 證書,有效問題導致此提示,非專業網絡管理人員,不知道如何處理,此處點是即可 證書有效開始時間是安裝時間8小時 證書有效結束…

【初識掃盲】逆概率加權

我們正在處理一個存在缺失數據的回歸模型&#xff0c;并且希望采用一種非參數的逆概率加權方法來調整估計&#xff0c;以應對這種缺失數據的情況。 首先&#xff0c;我們需要明確問題的背景。我們有樣本 { ( Y i , X i , r i ) : i 1 , … , n } \left\{\left(Y_i, \boldsym…

極客說|Azure AI Agent Service 結合 AutoGen/Semantic Kernel 構建多智能體解決?案

作者&#xff1a;盧建暉 - 微軟高級云技術布道師 「極客說」 是一檔專注 AI 時代開發者分享的專欄&#xff0c;我們邀請來自微軟以及技術社區專家&#xff0c;帶來最前沿的技術干貨與實踐經驗。在這里&#xff0c;您將看到深度教程、最佳實踐和創新解決方案。關注「極客說」&am…

【集成學習】Boosting算法詳解

文章目錄 1. 集成學習概述2. Boosting算法詳解3. Gradient Boosting算法詳解3.1 基本思想3.2 公式推導 4. Python實現 1. 集成學習概述 集成學習&#xff08;Ensemble Learning&#xff09;是一種通過結合多個模型的預測結果來提高整體預測性能的技術。相比于單個模型&#xf…

小米vela系統(基于開源nuttx內核)——如何使用信號量進行PV操作

如何使用信號量進行PV操作 前言信號量1. 信號量簡介2. NuttX中信號量的創建與使用2.1 Nuttx信號量的初始化和銷毀2.2 信號量的等待和發布 3. 信號量的實際應用&#xff1a;下載任務示例3.1 實際代碼3.2 代碼說明3.3 執行說明 4. 信號量的優勢與應用場景5. 常見應用場景&#xf…

CMake學習筆記(2)

1. 嵌套的CMake 如果項目很大&#xff0c;或者項目中有很多的源碼目錄&#xff0c;在通過CMake管理項目的時候如果只使用一個CMakeLists.txt&#xff0c;那么這個文件相對會比較復雜&#xff0c;有一種化繁為簡的方式就是給每個源碼目錄都添加一個CMakeLists.txt文件&#xff…

旅游網站設計與實現

文末附有完整項目代碼 在當今數字化時代&#xff0c;旅游網站成為人們獲取旅游信息的重要途徑。本文將詳細介紹旅游網站的設計與實現&#xff0c;讓你輕松了解其中的技術奧秘&#xff01; 一、項目背景 隨著社會經濟的發展&#xff0c;人們對精神消費愈發重視&#xff0c;旅游…

【C++】size_t究竟是什么?全面解析與深入拓展

博客主頁&#xff1a; [小????????] 本文專欄: C 文章目錄 &#x1f4af;前言&#x1f4af;一、什么是size_t&#xff1f;為什么需要size_t&#xff1f; &#x1f4af;二、size_t的特性與用途1. size_t是無符號類型示例&#xff1a; 2. size_t的跨平臺適應性示例對…

【物流管理系統 - IDEAJavaSwingMySQL】基于Java實現的物流管理系統導入IDEA教程

有問題請留言或私信 步驟 下載項目源碼&#xff1a;項目源碼 解壓項目源碼到本地 打開IDEA 左上角&#xff1a;文件 → 新建 → 來自現有源代碼的項目 找到解壓在本地的項目源代碼文件&#xff0c;點擊確定&#xff0c;根據圖示步驟繼續導入項目 查看項目目錄&#xff…

ssh2-sftp-client和ssh2配合使用js腳本快速部署項目到服務器

有時候因為服務器不能實現github或者gitlab的自動部署服務&#xff0c;所以就需要使用腳本來實現自動部署&#xff0c;可以省時省力&#xff0c;一勞永逸。這里就使用ssh2-sftp-client和ssh2來實現&#xff0c;即便是需要sudo權限&#xff0c;也是可以的。 1.先將本地打包后的…

深度解析Linux中的調試器gdb/cgdb的使用

Linux下我們編譯好的代碼&#xff0c;無法直接調試 gcc/g默認的工作模式是realse模式 程序要調試的話&#xff0c;必須是debug模式&#xff0c;也就是說編譯的時候要加-g選項 gdb攜帶調試信息的exe 我們現在在文件夾里面創建一個文件lesson11 里面創建一個累加的代碼&…

【Maui】動態菜單實現(綁定數據視圖)

前言 .NET 多平臺應用 UI (.NET MAUI) 是一個跨平臺框架&#xff0c;用于使用 C# 和 XAML 創建本機移動和桌面應用。 使用 .NET MAUI&#xff0c;可從單個共享代碼庫開發可在 Android、iOS、macOS 和 Windows 上運行的應用。 .NET MAUI 是一款開放源代碼應用&#xff0c;是 X…

Bash語言的語法糖

Bash語言的語法糖 引言 在現代編程語言中&#xff0c;“語法糖”是一個非常常見的術語&#xff0c;它指的是那些使代碼更加易讀、易寫的語法特性。盡管這些特性并不改變語言的功能&#xff0c;但它們能顯著提升開發者的編程體驗。在眾多編程語言中&#xff0c;Bash&#xff0…

linux---Nginx詳細教程(包含安裝,網站部署)

Nginx是一個高性能的HTTP和反向代理服務器&#xff0c;也可以用作郵件代理服務器&#xff0c;其以占有內存少、并發能力強、穩定性高、豐富的功能集、低系統資源消耗而聞名。以下是對Nginx的詳細教程&#xff1a; 一、Nginx簡介 Nginx由俄羅斯人開發&#xff0c;第一個公開版…

RNN之:LSTM 長短期記憶模型-結構-理論詳解-及實戰(Matlab向)

0.前言 遞歸&#xff01;循環神經網絡Recurrent Neural Network 循環神經網絡&#xff08;又稱遞歸神經網絡&#xff0c;Recurrent Neural Network&#xff0c;RNN&#xff09;。是一種用于處理序列數據的神經網絡結構&#xff0c;具有記憶功能&#xff0c;能夠捕捉序列中的時…

泛目錄和泛站有什么差別

啥是 SEO 泛目錄&#xff1f; 咱先來說說 SEO 泛目錄是啥。想象一下&#xff0c;你有一個巨大的圖書館&#xff0c;里面的書架上擺滿了各種各樣的書&#xff0c;每一本書都代表著一個網頁。而 SEO 泛目錄呢&#xff0c;就像是一個超級圖書管理員&#xff0c;它的任務就是把這些…

初識@ffmpeg/ffmpeg庫

前言 FFmpeg是一套可以用來記錄、轉換數字音頻、視頻,并且能夠利用它們來創建一個新的流媒體格式的自由軟件項目,它被廣泛應用在視頻處理、音頻處理以及直播領域。其中,@ffmpeg/ffmpeg 是一個將 FFmpeg 編譯為 WebAssembly(WASM)的庫,可支持幾乎所有的音視頻格式。 安裝…