数据结构第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.mzph.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;能跑出…