數據結構第06節:棧

棧(Stack)是一種后進先出(Last In First Out, LIFO)的數據結構,它只允許在一端,稱為棧頂(Top),進行添加(Push)和移除(Pop)數據項的操作。棧常用于解決遞歸、回溯、函數調用等問題。

棧的基本操作包括:

  1. Push: 向棧頂添加一個元素。
  2. Pop: 移除棧頂元素,并返回它。
  3. Peek/Top: 返回棧頂元素但不移除它。
  4. IsEmpty: 檢查棧是否為空。
  5. Size: 返回棧中的元素數量。

下面是使用Java實現棧的一個簡單示例:

import java.util.EmptyStackException;public class Stack<T> {private static class StackNode<T> {private T data;private StackNode<T> next;public StackNode(T data) {this.data = data;}}private StackNode<T> top;private int size;public Stack() {top = null;size = 0;}public void push(T item) {StackNode<T> t = new StackNode<T>(item);t.next = top;top = t;size++;}public T pop() {if (isEmpty()) {throw new EmptyStackException();}T item = top.data;top = top.next;size--;return item;}public T peek() {if (isEmpty()) {throw new EmptyStackException();}return top.data;}public boolean isEmpty() {return top == null;}public int size() {return size;}
}

在這個Java實現中,我們使用了泛型(<T>),使得棧可以存儲任何類型的數據。內部的StackNode類用于表示棧中的每個元素,它包含數據和指向下一個元素的鏈接。Stack類本身維護了一個指向棧頂的引用top,以及棧的大小size

使用這個棧類的方法如下:

public class Main {public static void main(String[] args) {Stack<Integer> stack = new Stack<>();stack.push(1);stack.push(2);stack.push(3);System.out.println("Top element is: " + stack.peek()); // 輸出3while (!stack.isEmpty()) {System.out.println(stack.pop());}}
}

在這個例子中,我們創建了一個整數類型的棧,然后向棧中添加了幾個元素。使用peek方法可以查看棧頂元素而不移除它,最后通過一個循環,使用pop方法來移除并打印所有元素,直到棧為空。

Demo1:實現一個簡易的文本編輯器中的撤銷(Undo)功能

在文本編輯器中,每當用戶執行一個操作,比如插入或刪除文本,這個操作就會被壓入一個棧中。如果用戶想要撤銷最近的操作,編輯器就會從棧中彈出最近的一個操作并執行它的逆操作。

以下是使用Java實現文本編輯器中撤銷功能的示例代碼:

interface EditAction {void execute();  // 執行操作void undo();     // 撤銷操作
}class TextEditor {private String text;private Stack<EditAction> undoStack;public TextEditor() {text = "";undoStack = new Stack<>();}public void type(String input) {// 保存當前文本狀態以便撤銷undoStack.push(new EditAction() {@Overridepublic void execute() {// 這里不需要實現,因為我們不會重新執行這個操作}@Overridepublic void undo() {// 撤銷操作:刪除最近輸入的文本text = text.substring(0, text.length() - input.length());}});text += input;}public void undo() {if (!undoStack.isEmpty()) {EditAction action = undoStack.pop();action.undo();}}public String getText() {return text;}
}public class Main {public static void main(String[] args) {TextEditor editor = new TextEditor();editor.type("Hello");editor.type(" World");System.out.println(editor.getText());  // 輸出 "Hello World"editor.undo();  // 撤銷 " World"System.out.println(editor.getText());  // 輸出 "Hello"editor.undo();  // 撤銷 "Hello"System.out.println(editor.getText());  // 輸出 ""}
}

在這個例子中,我們定義了一個EditAction接口,它包含executeundo方法。TextEditor類使用一個棧來存儲編輯操作,以便可以撤銷它們。type方法模擬用戶在文本編輯器中輸入文本,并將一個匿名內部類實例作為EditAction壓入棧中,該實例實現了撤銷最近輸入文本的功能。undo方法從棧中彈出最近的EditAction并調用其undo方法來撤銷操作。

這個簡單的撤銷功能展示了棧在實際應用中的一個典型用途,即維護一個操作的歷史記錄,以便可以按相反的順序撤銷它們。

Demo2:實現瀏覽器的前進和后退功能

瀏覽器歷史記錄可以通過兩個棧來實現:一個用于存儲后退歷史(Back Stack),另一個用于存儲前進歷史(Forward Stack)。當用戶訪問一個新頁面時,當前頁面會被推入后退棧,如果用戶點擊后退按鈕,頁面會從后退棧中彈出并推入前進棧;如果用戶點擊前進按鈕,頁面會從前進棧中彈出并推回后退棧。

以下是使用Java實現瀏覽器歷史記錄功能的示例代碼:

public class BrowserHistory {private Stack<String> backStack;private Stack<String> forwardStack;private String currentURL;public BrowserHistory() {backStack = new Stack<>();forwardStack = new Stack<>();currentURL = "Start Page";  // 假設初始頁面是起始頁}public void visit(String url) {backStack.push(currentURL);currentURL = url;forwardStack.clear();  // 清除前進棧,因為歷史已經改變}public String back() {if (backStack.isEmpty()) {return "No pages to go back to.";}forwardStack.push(currentURL);currentURL = backStack.pop();return currentURL;}public String forward() {if (forwardStack.isEmpty()) {return "No pages to go forward to.";}backStack.push(currentURL);currentURL = forwardStack.pop();return currentURL;}public String getCurrentURL() {return currentURL;}
}public class Main {public static void main(String[] args) {BrowserHistory history = new BrowserHistory();System.out.println(history.getCurrentURL());  // 輸出起始頁面history.visit("https://www.google.com");history.visit("https://www.example.com");System.out.println(history.getCurrentURL());  // 輸出 "https://www.example.com"history.back();System.out.println(history.getCurrentURL());  // 輸出 "https://www.google.com"history.forward();System.out.println(history.getCurrentURL());  // 輸出 "https://www.example.com"history.visit("https://www.anotherpage.com");System.out.println(history.getCurrentURL());  // 輸出 "https://www.anotherpage.com"history.back();System.out.println(history.getCurrentURL());  // 輸出 "https://www.google.com"}
}

在這個例子中,BrowserHistory類有兩個棧:backStack用于存儲后退歷史,forwardStack用于存儲前進歷史。visit方法模擬用戶訪問新頁面,將當前頁面URL壓入后退棧,并將新頁面URL設置為當前URL,同時清空前進棧。back方法模擬用戶點擊后退按鈕,將當前頁面URL壓入前進棧,并從后退棧中彈出之前的頁面URL作為當前頁面。forward方法模擬用戶點擊前進按鈕,將當前頁面URL壓入后退棧,并從前進棧中彈出下一個頁面URL作為當前頁面。

這個例子展示了棧在模擬瀏覽器歷史記錄中的實用性,允許用戶在訪問過的頁面之間前進和后退。

Demo3:表達式求值

使用棧可以方便地處理括號匹配和運算符優先級的問題。以下是一個使用棧進行基本算術表達式求值的Java實現:

import java.util.Stack;public class ExpressionEvaluator {public int evaluate(String expression) {Stack<Integer> numbers = new Stack<>();Stack<Character> operators = new Stack<>();String num = "";for (int i = 0; i < expression.length(); i++) {char c = expression.charAt(i);if (Character.isDigit(c)) {num += c;} else {if (!num.isEmpty()) {numbers.push(Integer.parseInt(num));num = "";}if (c == '(') {operators.push(c);} else if (c == ')') {while (!operators.isEmpty() && operators.peek() != '(') {numbers.push(applyOperation(numbers.pop(), numbers.pop(), operators.pop()));}operators.pop(); // Pop the '('} else if (isOperator(c)) {while (!operators.isEmpty() && hasHigherPrecedence(operators.peek(), c)) {numbers.push(applyOperation(numbers.pop(), numbers.pop(), operators.pop()));}operators.push(c);}}}if (!num.isEmpty()) {numbers.push(Integer.parseInt(num));}while (!operators.isEmpty()) {numbers.push(applyOperation(numbers.pop(), numbers.pop(), operators.pop()));}return numbers.pop();}private boolean isOperator(char c) {return c == '+' || c == '-' || c == '*' || c == '/';}private boolean hasHigherPrecedence(char op1, char op2) {return (op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-');}private int applyOperation(int b, int a, char op) {switch (op) {case '+': return a + b;case '-': return a - b;case '*': return a * b;case '/': return a / b;}return 0;}public static void main(String[] args) {ExpressionEvaluator evaluator = new ExpressionEvaluator();String expression = "3+5*(2-1)";System.out.println("Result: " + evaluator.evaluate(expression)); // 輸出結果}
}

在這個例子中,ExpressionEvaluator類使用兩個棧:numbers用于存儲操作數,operators用于存儲運算符。evaluate方法遍歷表達式字符串,當遇到數字時將其累加到num字符串中,當遇到運算符或括號時,執行相應的操作。

  • 如果遇到左括號(,將其壓入operators棧。
  • 如果遇到右括號),從棧中彈出運算符并執行相應的運算,直到遇到左括號。
  • 如果遇到運算符,檢查棧頂運算符的優先級,如果當前運算符優先級更高或相同,則先執行棧中的運算。
  • 最后,如果棧中還有運算符,繼續執行運算直到所有運算符都被處理完畢。

isOperator方法用于檢查字符是否是運算符,hasHigherPrecedence方法用于比較兩個運算符的優先級,applyOperation方法用于執行具體的運算。

這個例子展示了棧在處理帶有括號的表達式求值中的實用性,能夠有效地處理運算符優先級和括號匹配的問題。

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

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

相關文章

MySql Innodb 索引有哪些與詳解

概述 對于MYSQL的INNODB存儲引擎的索引&#xff0c;大家是不陌生的&#xff0c;都能想到是 B樹結構&#xff0c;可以加速SQL查詢。但對于B樹索引&#xff0c;它到底“長”得什么樣子&#xff0c;它具體如何由一個個字節構成的&#xff0c;這些的基礎知識鮮有人深究。本篇文章從…

【Spring Boot】JPA 的查詢方式

JPA 的查詢方式 1.使用約定方法名2.用 JPQL 進行查詢3.用原生 SQL 進行查詢3.1 根據 ID 查詢用戶3.2 查詢所有用戶3.3 根據 email 查詢用戶3.4 根據 name 查詢用戶&#xff0c;并返回分頁對象 Page3.5 根據名字來修改 email 的值3.6 使用事務 4.用 Specifications 進行查詢5.用…

Mac視頻下載工具,兼容14系統,Downie 4軟件下載

Downie 4 是一款由James Application開發的視頻下載軟件&#xff0c;支持Mac操作系統。該軟件允許用戶從各種視頻網站上下載視頻內容&#xff0c;以便于在本地設備上觀看&#xff0c;無需依賴互聯網連接。自動下載&#xff1a;可以設置Downie 4自動下載指定網站上的視頻&#x…

當+=的時候,為什么會出現NaN?

問: var textToDisplay; // "testing"; textToDisplay "testing"; textToDisplay 1; var someNumber 1; var oneMoreNumber; oneMoreNumber textToDisplay someNumber; //results in NaN console.log(oneMoreNumber); 這里的結果是NaN? 回答: 是…

【LinuxC語言】線程池的原理和實現

文章目錄 前言為什么需要線程池線程池的原理總結前言 在現代計算中,多線程編程已經成為一種常見的做法,它可以幫助我們更有效地利用多核處理器的能力。然而,頻繁地創建和銷毀線程會帶來一定的開銷。為了解決這個問題,我們可以使用一種稱為“線程池”的技術。線程池是一種在…

黑馬點評-Redis的緩存擊穿,緩存雪崩,緩存穿透,互斥鎖,邏輯過期

文章目錄 1.緩存穿透2.緩存雪崩3.緩存擊穿3.1 互斥鎖3.2 基于邏輯過期 1.緩存穿透 解決辦法 寫入NULL值到Redis緩存&#xff0c;以后就會命中Redis的控制緩存而不會出現請求直接打到數據庫的問題&#xff01; 代碼 2.緩存雪崩 這個概念很好理解&#xff0c;雪崩就是無數的…

【LLM大模型書】從零開始大模型開發與微調:基于PyTorch與ChatGLM (附PDF)

今天又來給大家推薦一本大模型方面的書籍<從零開始大模型開發與微調&#xff1a;基于PyTorch與ChatGLM>。 本書使用PyTorch 2.0作為學習大模型的基本框架&#xff0c;以ChatGLM為例詳細講解大模型的基本理論、算法、程序實現、應用實戰以及微調技術&#xff0c;為讀者揭…

設備樹在Linux系統的屬性

設備樹源文件 設備樹源文件擴展名為.dts&#xff0c;我們在前面移植 Linux 的時候卻一直在使用.dtb 文件&#xff0c;那么 DTS 和 DTB 這兩個文件是什么關系呢&#xff1f; DTS 是設備樹源碼文件&#xff0c; DTB 是將 DTS 編譯以后得到的二進制文件。將.dts 編譯為.dtb 需要什…

【微信小程序開發實戰項目】——如何制作一個屬于自己的花店微信小程序(2)

&#x1f468;?&#x1f4bb;個人主頁&#xff1a;開發者-曼億點 &#x1f468;?&#x1f4bb; hallo 歡迎 點贊&#x1f44d; 收藏? 留言&#x1f4dd; 加關注?! &#x1f468;?&#x1f4bb; 本文由 曼億點 原創 &#x1f468;?&#x1f4bb; 收錄于專欄&#xff1a…

FreeRTOS和UCOS操作系統使用筆記

FreeRTOS使用示例 UCOS使用示例 信號量使用 信號量訪問共享資源區/ OS_SEMMY_SEM; //定義一個信號量&#xff0c;用于訪問共享資源OSSemCreate ((OS_SEM* )&MY_SEM, //創建信號量&#xff0c;指向信號量(CPU_CHAR* )"MY_SEM", //信號量名字(OS_SEM_CTR )1, …

軟件模型分類及特點

在軟件開發的世界里&#xff0c;我們經常會遇到業務模型、系統模型和軟件模型這三個層次。這些模型各有特點&#xff0c;相互之間也有著緊密的聯系。通過理解這三個層次之間的映射關系&#xff0c;我們能更好地理解和掌握軟件開發的全過程 1. 業務模型 業務模型描述了組織的業…

政務單位網站SSL證書選擇策略

在數字化快速發展的今天&#xff0c;政務單位網站作為政府與公眾溝通的重要橋梁&#xff0c;其安全性和可信度顯得尤為重要。SSL證書作為保障網站安全的重要手段&#xff0c;其選擇對于政務單位網站來說至關重要。本文將探討政務單位網站在選擇SSL證書時應該考慮的因素&#xf…

如何使用python網絡爬蟲批量獲取公共資源數據教程?

原文鏈接&#xff1a;如何使用python網絡爬蟲批量獲取公共資源數據教程&#xff1f;https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247608240&idx4&snef281f66727afabfaae2066c6e92f792&chksmfa826657cdf5ef41571115328a09b9d34367d8b11415d5a5781dc4c…

【AI提升】如何使用大模型:本機離線和FastAPI服務調用

大模型本身提供的功能&#xff0c;類似于windows中的一個exe小工具&#xff0c;我們可以本機離線調用然后完成具體的功能&#xff0c;但是別的機器需要訪問這個exe是不可行的。常見的做法就是用web容器封裝起來&#xff0c;提供一個http接口&#xff0c;然后接口在后端調用這個…

KV260視覺AI套件--PYNQ-DPU-Resnet50

目錄 1. 簡介 2. 代碼解析 3. 全部代碼展示 4. 總結 1. 簡介 本文以 Resnet50 為例&#xff0c;展示使用 PYNQ 調用 DPU 運行 Resnet50 網絡的詳細過程&#xff0c;并對其中關鍵代碼做出解釋。 PYNQ是一個針對Xilinx Zynq平臺的Python開發框架&#xff0c;它允許開發者使…

KEYSIGHT是德科技 E5063A ENA 系列網絡分析儀

E5063A ENA 矢量網絡分析儀 18GHz 2端口 降低無源射頻元器件的測試成本 Keysight E5063A ENA 是一款經濟適用的臺式矢量網絡分析儀&#xff0c;可用于測試簡單的無源元器件&#xff0c;例如頻率最高達到 18 GHz 的天線、濾波器、電纜或連接器。 作為業界聞名的 ENA 系列…

深入解析 Laravel 事件系統:架構、實現與應用

Laravel 的事件系統是框架中一個強大且靈活的功能&#xff0c;它允許開發者在應用程序中定義和使用自定義事件和監聽器。這個系統基于觀察者模式&#xff0c;使得代碼解耦和可維護性大大提高。在本文中&#xff0c;我們將深入探討 Laravel 事件系統的工作原理、如何實現自定義事…

python @裝飾器的用法

裝飾器&#xff08;decorators&#xff09;是 Python 中的一種高級特性&#xff0c;它允許開發者修改函數或方法的行為&#xff0c;而不改變其定義。裝飾器通常用于日志記錄、權限檢查、性能測量等場景。裝飾器是通過在函數定義的前一行加上 decorator_name 來使用的。 基本用…

Qt簡單文本查找

Qt版本&#xff1a; Qt6 具體代碼&#xff1a; 1. 頭文件 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>class QLineEdit; class QDialog; class QPushButton; class QVBoxLayout; class QTextEdit;QT_BEGIN_NAMESPACE namespace Ui…

為什么AI算法工程師要求C++?

在開始前剛好我有一些資料&#xff0c;是我根據網友給的問題精心整理了一份「c&#xff0b;&#xff0b;的資料從專業入門到高級教程」&#xff0c; 點個關注在評論區回復“666”之后私信回復“666”&#xff0c;全部無償共享給大家&#xff01;&#xff01;&#xff01;能跑出…