棧(Java)

提示:多練才是王道,加油?(?????)?

棧Java

    • 1. 棧
    • 2. Java中棧的其中兩種實現方式
      • 2.1 Stack類
        • 2.1.1 Stack的模擬實現
      • 2.2 LinkedList類
    • 3. 典型習題講解
      • 3.1 逆波蘭表達式求值
      • 3.2 匹配括號
      • 3.3 合理彈出序列
      • 3.4 最小棧


1. 棧

棧是一種特殊的線性表,其只允許在固定的一端進行插入和刪除操作.進行數據插入和刪除的一端被稱為棧頂,另一端稱為棧底.棧中的元素遵循先進后出的原則.棧的三大核心操作:

  1. push(E item):將元素壓入棧頂
  2. pop():彈出并返回棧頂元素.如果棧為空,則拋出異常
  3. peek():查看棧頂元素但不彈出.如果棧為空,則拋出異常

在這里插入圖片描述

如圖所示,在棧頂進行入棧和出棧操作.

2. Java中棧的其中兩種實現方式

2.1 Stack類

java.util.Stack類,是Java最早提供的棧實現,官方已經不推薦使用了,但是在很多編程算法題中,Stack仍然被廣泛使用.

為什么不推薦?

Stack繼承自Vector:

  • 性能差:Vector是線程安全的,所有方法都用synchronized關鍵字修飾,即使在單線程環境下也會帶來不必要的開銷.
  • 設計缺陷:它繼承制Vector,導致它擁有了許多緋斬操作(如get(int index),insertElementAt),這破壞了棧的封裝性和"后進先出"的原則.

既然不推薦,為什么很多刷題平臺還是廣泛使用呢?

  • 歷史關心 & 教學傳統:很多算法教程、大學課程和早期的編程書籍都是基于Stack類來講解棧概念的。這些教學材料有很強的延續性,導致一代代的程序員在學習階段就習慣了使用Stack。
  • 要求不同:在算法題中,通常只關心核心邏輯的正確性,而不是生產級別的代碼質量.Stack的線程安全開銷和設計缺陷在算法題的輸入規模下幾乎可以忽略不計,不會導致超時.
  • 功能足夠:對于求解一道算法題Stack提供的 push, pop, peek, empty 方法已經完全夠用了。雖然它有多余的方法,但只要你不去用它們,就不會造成問題。

好了言歸正傳,現在我們來看一下這個Stack:

通過源碼可以看到,Stack的核心方法就以下幾種:

在這里插入圖片描述

也就是:

方法功能
Stack()構造一個空的棧
E push(E e)將e入棧,并返回e
E pop()將棧頂元素出棧并返回(刪除)
E peek()獲取棧頂元素(瞄一眼,不刪除)
int size()獲取棧中有效元素個數
boolean empty()檢測棧是否為空
2.1.1 Stack的模擬實現
public class MyStack {//底層使用整形數組elem啦存儲元素public int[] elem;//usedSize兩個作用:①表示當前棧中元素個數②始終指向棧頂元素的下一個位置,下一個入棧元素的下標public int usedSize;//構造方法將數組初始化為大小為10的整型數組public MyStack() {this.elem=new int[10];}//壓棧操作public void push(int val) {if(isFull()) {//擴容,使用Arrays.copyOf方法,將新數組長度擴容為原來的2倍elem= Arrays.copyOf(elem,2*elem.length);}elem[usedSize++]=val;}public boolean isFull() {return usedSize==elem.length;}//出棧操作public int pop() {if(empty()) {return -1;//這里更合適做法是拋出一個異常}int toPop=elem[usedSize-1];//保留棧頂元素usedSize--;//邏輯刪除return toPop;//最后返回棧頂元素}public boolean empty() {return usedSize==0;}//查看棧頂元素,邏輯與pop一致,除了不用usedSize--public int peek() {if(empty()) {return -1;//這里更合適做法是拋出一個異常}int toPeek=elem[usedSize-1];return toPeek;}
}

2.2 LinkedList類

上面Stack類的底層是動態數組,所以也叫順序棧,用鏈表能實現棧嗎?—可以.

LinkedList是雙向鏈表,天然實現了棧所需的所有操作,并且效率很高.因此完全可以基于LinkedList實現棧.

在這里插入圖片描述

順著pushpop方法點過去可以看到兩者的源碼:

在這里插入圖片描述

在這里插入圖片描述

可以看到,push調用的是addFirst方法,即頭插;pop調用的是removeFirst方法,即頭刪,因此用LinkedList實現的棧棧頂在鏈表的頭,不是尾.

對比使用Stack類(在數組尾部進行入棧出棧,時間復雜度為O(1)):

  • 這樣入棧出棧操作也很高效,只涉及改變幾個指針的指向,時間復雜度同為O(1).
  • 然而數組滿后需要昂貴的擴容操作(申請新數組,復制所有元素),雙向鏈表無擴容開銷,且不會造成內存浪費.

3. 典型習題講解

3.1 逆波蘭表達式求值

150. 逆波蘭表達式求值 - 力扣(LeetCode)

這是一個棧的典型使用場景之一.求解這道題需要先弄明白逆波蘭表達式的計算原理.

首先,什么是逆波蘭表達式?

也叫后綴表達式,核心特點是運算符位于其對應的兩個操作數之后.我們日常接觸的都是中綴表達式,比如:a+b*c+(d*e+f)*g,其對應的后綴表達式為:abc*+de*f+g*+.

這是怎么轉化的?有人總結出了以下步驟:

  1. 先將中綴表達式的每個運算周圍用括號括起來:a+b*c+(d*e+f)*g->((a+(b*c))+(((d*e)+f)*g))
  2. 然后將每個運算符都向右移到包裹它的括號右邊:((a+(b*c))+(((d*e)+f)*g))->((a(bc)*)+(((de)*f)+g)*)+
  3. 最后將括號全部去掉:((a(bc)*)+(((de)*f)+g)*)+->abc*+de*f+g*+

可以明顯看到后綴表達式的一個特征:沒有括號,第3步將括號全部去掉了.

那為什么需要后綴表達式?

中綴表達式對人類很友好,但是對于計算機卻很麻煩:需要處理運算符優先級(如乘除優先于加減)和加括號來確定運算順序,這個過程比較復雜.

后綴表達式完美地解決了這個問題:

  • 無需括號
  • 無需優先級規則
  • 只需要一個棧即可輕松完成計算.

那它的計算原理是什么?

  1. 首先創建一個棧,準備將后綴表達式按照一定規律從左向右依次壓入:
    • 若遇到數字,壓入棧中,一直是數字就一直壓
    • 若遇到的是運算符(+ - * /),則出棧兩次,第一次的是右操作數,第二次是左操作數,運算過后,將結果繼續壓入棧中
    • 循環上述兩個步驟,直至后綴表達式被掃描完
  2. 掃描完后,棧中剩下的即為整個表達式的運算結果

在這里插入圖片描述

然后思路就很清晰啦:

class Solution {public int evalRPN(String[] tokens) {Stack<Integer> stack=new Stack<>();for(int i=0;i<tokens.length;i++) {String tmp=tokens[i];if(isOperation(tmp)) {int num2=stack.pop();//右操作數int num1=stack.pop();//左操作數switch(tmp) {case "+":stack.push(num1+num2);break;case "-":stack.push(num1-num2);break;case "*":stack.push(num1*num2);break;case "/":stack.push(num1/num2);break;}}else {Integer num=Integer.valueOf(tmp);//將字符轉為整型stack.push(num);}}return stack.pop();//遍歷完數組,棧中剩下的就是運算結果}//判斷是否是表達式public boolean isOperation(String s) {//引用類型進行比較要用equals,不能用==if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {return true;}else {return false;}}
}

3.2 匹配括號

20. 有效的括號 - 力扣(LeetCode)

在這里插入圖片描述

該題目要求第一次出現的右括號必須與它的前一個括號匹配,形狀相同,朝向相反.

解題思路:創建一個棧,將字符串從左到右依次便利:

  • 若是左括號,就痛快入棧;
  • 若是右括號,彈出棧頂元素與其匹配:
    • 匹配的話(形狀相同,朝向相反),就麻溜"出棧走人",然后下一位;
    • 不匹配的話,直接返回false
  • 遍歷完了就只用看下棧空不空,空了說明全部都匹配上了,返回true;非空返回false.

思路清晰,coding~

class Solution {public boolean isValid(String s) {Stack<Character> stack=new Stack<>();for(int i=0;i<s.length();i++) {char ch=s.charAt(i);if(ch=='{' || ch=='[' || ch=='(') {//ch為左括號,入棧stack.push(ch);}else {//ch為右括號if(stack.empty()) {return false;//若是遇到了落單的右括號,直接false}else {char tmp=stack.peek();if(tmp=='{' && ch=='}' ||tmp=='[' && ch==']'|| tmp=='(' && ch==')' ) {stack.pop();}else {return false;}}}}return stack.empty();}
}

注意:判斷Stack是否為空有兩個方法isEmpty()和empty()(empty是Stack自己聲明的,isEmpty則是Sytack從其父類Vector繼承而來的),在idea上兩者都能用,但是在力扣上用isEmpty()會報錯,這可能是因為力扣的版本或環境問題,也可能是提供的Stack類是一個預定義的,模擬的類,就像2.1.1的模擬實現一樣.

3.3 合理彈出序列

棧的壓入、彈出序列_牛客題霸_牛客網

在這里插入圖片描述

這種題型在選擇題中常見,但是若是要將寫選擇題那種有點"只可意會不可言傳"的感覺轉為代碼,還是有點差距的~

在這里插入圖片描述

這題的思路是:定義兩個下標,分別遍歷入棧和出棧序列,假設i遍歷入棧序列,j遍歷出棧序列;

  • 定義一個棧,將i對一個元素入棧,然后將i與j下標對應的值進行比較,若不相等,i++,j不動
  • i一直++,直到i,j對應值相等,此時,將i對應元素出棧,j++
  • 若是棧頂元素與j對應元素一直相等,則一直出棧,j也一直++;不相等了,再重復上一步操作
  • 最后i和j都會遍歷完,若此時棧為空,則說明該出棧序列是合理的,反之不合理.
public boolean IsPopOrder (int[] pushV, int[] popV) {Stack<Integer> stack=new Stack<>();int j=0;for(int i=0;i<pushV.length;i++) {stack.push(pushV[i]);while(!stack.empty() && j<popV.length && stack.peek()==popV[j]) {stack.pop();j++;}}return stack.empty();}

3.4 最小棧

155. 最小棧 - 力扣(LeetCode)

在這里插入圖片描述

題目明確要求在常數時間內檢索到最小元素,若只定義一個棧,那么尋找最小元素勢必要遍歷一遍stack,時間復雜度為O(n),不符合要求,因此,此題要建立兩個棧:

  1. 一個普通棧,正常壓入彈出元素,每次使用push,pop,top這三個方法時均要對這個普通棧進行操作.
  2. 還有一個存放最小元素的棧,該棧的壓入彈出不能那么隨心所欲了:
    • 元素push時要先與該棧棧頂元素比較,只有不大于棧頂元素才能壓入棧中,否則便不能;
    • pop時要將棧頂元素與普通棧pop掉的元素比較,若相等,才能pop掉
    • top不能操作該棧
    • getMin只能操作該棧,用于peek該棧的棧頂元素

coding~

class MinStack {Stack<Integer> stack;Stack<Integer> minStack;public MinStack() {stack=new Stack<>();minStack=new Stack<>();}public void push(int val) {stack.push(val);if(minStack.empty()) {minStack.push(val);} else if(val<=minStack.peek()) {minStack.push(val);}}public void pop() {if(stack.empty()) return;int tmp=stack.pop();if(tmp==minStack.peek()) {minStack.pop();}}public int top() {if(stack.empty()) return -1;else return stack.peek();}public int getMin() {if(minStack.empty()) return -1;else return minStack.peek();}
}

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

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

相關文章

LayaAir鼠標(手指)控制相機旋轉,限制角度

切換天空盒腳本掛載到相機身上 const { regClass, property } Laya;regClass() export class SmoothCameraController extends Laya.Script {declare owner: Laya.Camera;// 旋轉靈敏度property({ type: Number, name: "旋轉靈敏度" })public rotationSensitivity:…

【數據結構入門】排序算法(4)歸并排序

目錄 1.排序的原理 1.1 保證子數組有序 1.2 時間復雜度 2. 遞歸實現 2.1 思路 2.2 代碼 3. 非遞歸實現 3.1 思路 3.2 代碼 4.面試題 4.1 題目 4.2 思路 1.排序的原理 歸并排序是外排序&#xff0c;所謂外排序就是說能夠對文件中的數據進行排序。 ①首先&#xff…

FLEXSPI_Init 硬件故障問題

使用官方例程發現FLEXSPI_Init會引起硬件故障&#xff0c;查閱相關帖子發現主要有兩個可能&#xff1a;1、外部閃存配置差異修改 LUT&#xff08;查找表&#xff09;命令&#xff1a;示例中擦除扇區命令為 0xD7&#xff0c;寫狀態寄存器命令為 0x01&#xff0c;需分別改為 閃存…

如何用 Rust 重寫 SQLite 數據庫(一):項目探索

要使用 Rust 重寫 SQLite 數據庫&#xff0c;我們需要實現一個簡化的關系型數據庫核心功能&#xff08;如 SQL 解析、存儲引擎、事務管理&#xff09;。以下是一個分步實踐指南&#xff0c;包含關鍵代碼示例。一、項目規劃 我們將實現一個超簡化數據庫 MiniSQL&#xff0c;支持…

JVM之堆(Heap)

一、堆的核心特性 唯一性與共享性 每個JVM實例僅有一個堆&#xff0c;所有線程共享&#xff0c;但可通過線程私有緩沖區&#xff08;TLAB&#xff09;減少多線程分配沖突。內存結構演變 JDK 7及之前&#xff1a;堆分為新生代&#xff08;Young&#xff09;、老年代&#xff08;…

單片機的RAM與ROM概念

RAM與ROM1、RAM與ROM2、 bss、data、heap、stack、text詳細講解3、詳細探討 TCM、OCRAM 和 HBNRAM 之間的區別及其具體作用。3.1、TCM&#xff08;Tightly Coupled Memory&#xff09;3.2、 OCRAM&#xff08;On Chip RAM&#xff09;3.3、HBNRAM (Hibernate RAM)3.4、總結1、R…

實驗3:事件處理(2學時)

實驗目的&#xff08;1&#xff09;熟練掌握 v-on 指令的用法&#xff0c;學會使用 v-on 指令監聽 DOM 元素的事件&#xff0c;并通過該事件觸發調用事件處理程序。&#xff08;2&#xff09;掌握v-on 指令修飾符的基本用法。實驗內容實現購物車功能的拓展&#xff08;商品數量…

商品庫存扣減方案

文章目錄1. Lua腳本 Redis&#xff08;業界首選&#xff0c;綜合最優&#xff09;2. Redis原子命令&#xff08;DECRBY 結果校驗&#xff09;3. Redis事務&#xff08;MULTI/EXEC&#xff09;4. 分布式鎖&#xff08;基于Redis實現&#xff09;5. Redisson客戶端封裝&#xf…

關于在阿里云DMS誤操作后如何恢復數據的記錄

前言 昨天因客戶員工操作錯誤&#xff0c;導致快遞單號和訂單互換。客戶員工那邊讓筆記修改數據。 于是筆者寫下如下SQL來操作&#xff0c;導致了災難性事故。 update t_order_fed_ex_record set tracking_number 884102170661, master_tracking_number 884102170661, push…

【操作系統核心知識梳理】線程(Thread)重點與易錯點全面總結

在多任務操作系統中&#xff0c;線程是比進程更輕量的執行單元&#xff0c;理解線程的特性和實現方式是掌握并發編程的基礎。本文系統梳理了線程相關的核心知識點和常見誤區&#xff0c;助你夯實操作系統基礎。一、線程的基本概念與引入目的 1.1 什么是線程&#xff1f; 線程是…

深入理解 Python 中的 `__call__` 方法

化身為可調用的對象&#xff1a;深入理解 Python 中的 __call__ 方法 引言&#xff1a;函數與對象的邊界模糊化 在 Python 中&#xff0c;我們最熟悉的概念莫過于函數&#xff08;Function&#xff09; 和對象&#xff08;Object&#xff09;。函數是可調用的&#xff08;calla…

云服務器使用代理穩定與github通信方法

使用SSH反向隧道 (SSH Reverse Tunneling) 利用SSH連接在您的本地電腦和云服務器之間建立一個反向的加密通道。 原理&#xff1a; 從本地電腦發起一個SSH命令到您的云服務器&#xff0c;這個命令會告訴云服務器&#xff1a;“請監聽您自己的某個端口&#xff08;例如&#xff1…

7.k8s四層代理service

Service的基本介紹 Cluster IP&#xff1a;每個 Service 都分配了一個Cluster IP&#xff0c;它是一個虛擬的內部IP地址&#xff0c;用于在集群內部進行訪問。這個虛擬IP是由Kubernetes自動分配的&#xff0c;并且與Service對象一一對應。 端口映射&#xff1a;Service可以映射…

Qt 工程中 UI 文件在 Makefile 中的處理

Qt 工程中 UI 文件在 Makefile 中的處理 在 Qt 工程中&#xff0c;.ui 文件&#xff08;Qt Designer 界面文件&#xff09;需要通過 uic&#xff08;用戶界面編譯器&#xff09;工具轉換為對應的頭文件。以下是幾種情況下如何處理 UI 文件&#xff1a;1. 使用 qmake 自動生成 M…

ZLMediaKit性能測試

一、環境 系統&#xff1a;虛擬機 Ubuntu22.04 64bit配置: 4核8G設置&#xff1a;ulimit -n 102400 二、安裝 依賴安裝sudo apt update sudo apt install ffmpeg sudo apt install nloadzlm服務安裝參考&#xff1a;https://blog.csdn.net/hanbo622/article/details/149064939?…

智能文檔處理業務,應該選擇大模型還是OCR專用小模型?

智能文檔處理業務中&#xff0c;最佳策略不是二選一&#xff0c;而是“大小模型協同”。用專用小模型處理高頻、標準化的核心文檔流&#xff0c;實現極致效率與成本控制&#xff1b;用大模型賦能非標、長尾文檔的靈活處理&#xff0c;加速業務創新。 OCR小模型會被大模型取代嗎…

android 如何判定底部導航欄顯示時 不是鍵盤顯示

在 Android 中判定底部導航欄是否顯示時&#xff0c;核心痛點是 區分 “導航欄的底部 Insets” 和 “軟鍵盤彈出的底部 Insets”—— 兩者都會導致 getSystemWindowInsetBottom() 返回非零值&#xff0c;直接判斷會誤將鍵盤彈出當成導航欄顯示。以下是基于 WindowInsets 類型區…

你知道服務器和電腦主機的區別嗎?

我們都知道服務器和臺式主機有著不同之處&#xff0c;但具體說出個一二三來很多人還是一頭霧水&#xff0c;也就是知其然不知其所以然&#xff0c;都是CPU主板 內存 硬盤 電源&#xff0c;撐死就差一個顯卡不同&#xff0c;但其實服務器和我們正常使用的臺式主機差距很大&#…

什么是包裝類

什么是包裝類 在Java中&#xff0c;包裝類&#xff08;Wrapper Class&#xff09;是為基本數據類型提供的對應的引用類型。Java中的基本數據類型&#xff08;如int、char、boolean等&#xff09;不是對象&#xff0c;為了在需要對象的場景中使用基本數據類型&#xff08;如集合…

用Python打造專業級老照片修復工具:讓時光倒流的數字魔法

在這個數字化時代&#xff0c;我們手中珍藏著許多泛黃、模糊、甚至有劃痕的老照片。這些照片承載著珍貴的回憶&#xff0c;但時間的侵蝕讓它們失去了往日的光彩。今天&#xff0c;我將帶您一起用Python開發一個專業級的老照片修復工具&#xff0c;讓這些珍貴的記憶重現光彩。為…