栈(Stack)

news2024/11/24 20:44:42

目录

一.栈(Stack)

1.概念

2.栈的使用

3.栈的模拟实现

 二.栈相关习题

1.逆波兰表达式求值

(1)链接

(2)解析

(3)题解

 2.括号匹配

(1)链接

(2)解析

(3)题解

 3.栈的压入弹出序列

(1)链接

(2)解析

(3)题解

4.最小栈

(1)链接

(2)解析

(3)题解


一.栈(Stack)

1.概念

一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出(先进后出

LIFO(Last In First Out)的原则。

压栈(进栈)栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈栈的删除操作叫做出栈。出数据在栈顶。

栈在我们日常生活中的例子:

2.栈的使用

import java.util.Stack;

public class MyStack {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        stack.push(4);
        stack.push(5);//以上为压栈操作
        Integer x = stack.peek();
        System.out.println(x);//获取栈顶元素
        System.out.println(stack.size());//获取栈内有效元素个数
        System.out.println(stack.isEmpty());//检查栈是否为空
        
        Integer y = stack.pop();
        System.out.println(y);//出栈操作
        int z = stack.size();
        System.out.println(z);
    }
}

 

注意:

  • 我们在判断栈中是否为空时,可以使用栈这个类中的Empty进行判断,也可以使用Vector类当中的isEmpty进行判断,因为Stack类是继承Vector类的
  • 在以上例子中我们进行出栈操作,获取有效元素个数,获取栈顶元素时,我们既可以通过Integer来进行接收,也可以使用int进行拆箱操作来进行接收

3.栈的模拟实现

栈是一个特殊的顺序表,所以采用链表和数组的方式都可实现,但是,一般采用数组的方式实现

usedSize来表示栈中有效元素个数,入栈一个元素则usedSize++,出栈一个元素则usedSize--,所以此时usedSize有两个含义:

①可以表示当前数据存放的个数

②表示当前存放数据的下标 

说明:usedSize初始值为0,我们往栈里添加元素,将元素添加到下标为0的数组中,此时usedSize为0表示当前存放数据的下标,添加1个元素后,进行usedSize++操作,此时usedSize表示当前数据存放的个数

push()入栈:

  1. 先判断数组是否满了,若满了则进行扩容
  2. 若没满则进行入栈操作,即 将元素按顺序添加到数组中,通过usedSize计数下标来进行添加,添加一个元素,下标就往后走一个,直到满了之后进行扩容

pop()出栈:

  1. 先判断栈中是否有元素,没有则抛出异常
  2. 有元素则进行出栈,我们返回的是栈顶的元素,即usedSize-1的元素即可,
    通过减少 usedSize 的值,我们实际上是在告诉栈:“我们刚刚移除了一个元素,所以现在的栈比刚才小了一个元素

    为什么返回的是useSize-1的值而不是usedSize的值?
    因为在我们进行入栈操作时是先使用的usedSize来表示下标来进行添加元素操作的,添加一个元素usedSize就++,所以此时我们想返回栈顶元素时,栈顶元素的下标就是usedSize-1

    如何进行空间释放?
    因为我们栈中的数据时简单类型,只需要将表示数组中元素个数的usedSize--即可,当下一次进行入栈操作时直接将元素覆盖即可,但如果是引用类型的数据则需要进行置空操作
import java.util.Arrays;

public class MyStack {
    public int[] elem;
    public int usedSize;

    public MyStack() {
        this.elem = new int[10];
    }
    public void push(int val){
        if (isFull()) {
            //如果满则进行扩容,此处进行两倍扩容
           elem = Arrays.copyOf(elem,elem.length * 2);
        }
        elem[usedSize] = val;//将元素按顺序进行压栈
        usedSize++;
    }
    public boolean isFull() {
        //判断栈是否满
        return usedSize == elem.length;
    }
    public int pop() {
        //将栈顶元素出栈,要求弹出栈顶上一个元素的值

        //如果usedSize为空则无法抛出
        if (empty()) {
            throw new RuntimeException("栈中没有元素,不能出栈");
        }
        int oldV = elem[usedSize - 1];//存储上一个元素的值
        usedSize--;
        return oldV;
    }
    public boolean empty() {
        return usedSize == 0;
    }
    public int peek() {
        if (empty()) {
            return 0;
        }
        return elem[usedSize - 1];
    }
}

 二.栈相关习题

1.逆波兰表达式求值

(1)链接

逆波兰表达式求值icon-default.png?t=N7T8https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/

(2)解析

解题之前我们先理解什么叫中缀表达式和后缀表达式
 

中缀表达式
该表达式就是我们平常学习中常用的表达式,例如:(3 + 4) * 5


它的特点为:

  • 操作符(如加、减、乘、除等)位于操作数之间。
  • 需要括号来改变默认的运算优先级。

后缀表达式(逆波兰表达式)
例如:3 4 + 5 *(相当于 (3 + 4) * 5,结果是 35)

  • 操作符位于操作数之后。
  • 不需要括号来指明运算优先级,因为操作符的优先级由它们在表达式中的位置决定。

如何将中缀表达式转为后缀表达式呢?

这里我们通过两个例子来进行演示:

  1. 我们将    (3 + 4) * 5    这个中缀表达式转为后缀表达式
    ①我们将这个表达式按照先乘除后加减的规则,将表达式中所有的式子加上括号,即:
            ((3 + 4) * 5)
    ②将运算符移至当前表达式所在括号的外面,即:
            
    ((3 4)+ 5)*
    ③将所有的括号去掉,即
            3  4+  5*
    此时我们就完成了转换
  2.            1 +2*3 +(4*5 +6)*7         再来个更复杂的转换

    对应转换为种植表达式就是:1 +2*3 +(4*5 +6)*7 = 189

总结:

  1. 按照先乘除后加减的规则进行添加括号操作
  2. 将运算符移至当前表达式所在括号的外面
  3. 将所有的括号去掉

后缀表达式是如何存储到栈中并计算呢?

        是操作数就放入栈中,直到遇到运算符,弹出栈顶的第一个元素作为右操作数,第二个作为左操作数,计算出的结果再放入栈中,重复这一操作即可

(3)题解

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)) {
                //因为我们获取的是数组中的元素,每个元素的类型是字符串,所以我们进行类型转换
                Integer val = Integer.valueOf(tmp);
                stack.push(val);
            }else{
                Integer val2 = stack.pop();
                Integer val1 = stack.pop();
                switch(tmp) {
                    case "+":
                        stack.push(val1 + val2);
                    break;
                    case "-":
                        stack.push(val1 - val2);
                    break;
                    case "*":
                        stack.push(val1 * val2);
                    break;
                    case "/":
                        stack.push(val1 / val2);
                    break;
                }
            }
        }
        return stack.pop();
    }
    //判断是否是有效的运算符
    public boolean isOperation(String s) {
        if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {
            return true;
        }
        return false;
    }
}

 2.括号匹配

(1)链接

括号匹配icon-default.png?t=N7T8https://leetcode.cn/problems/valid-parentheses/description/

(2)解析

分四种情况:

  1. 括号匹配的情况下,栈最终为空且字符串已经遍历完成。(){}
  2. 左括号和右括号不匹配      (]{}
  3. 字符串没有遍历完成,遇到了右括号,但是栈为空。())))
  4. 字符串遍历完成,但是栈当中仍然存在左括号。 (()

只有当获取的字符串为左括号时我们才进行入栈操作,当遇到右括号时我们分为两种情况:①当第一个元素就是右括号,此时栈为空,直接返回false即可②如果右括号不是第一个元素,我们就看当前元素是否与栈顶的元素匹配,若匹配则将栈顶的元素出栈,若不匹配则返回false


当整个循环走完,栈为空时则为true 例:()))),栈内不为空则为false  例:()((

(3)题解

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for(int i = 0;i < s.length();i++) {
            //1.判断第一个元素是否是左括号
            char ch = s.charAt(i);
            if(ch == '(' || ch == '[' || ch == '{' ) {
                stack.push(ch);
            } else {
                //2.遇到了右括号
                if(stack.empty()) {
                    //如果第一个元素为右括号,此时栈内是空的,直接返回false
                    return false;
                } else {
                    char chLeft = stack.peek();//获取栈顶元素
                    //判断左右括号是否匹配
                    if(chLeft == '(' && ch == ')' || 
                    chLeft == '[' && ch == ']' || chLeft == '{' && ch == '}') {
                        stack.pop();
                    }else {
                        return false;
                    }
                }
            }
        }
        //当循环走完时,栈内为空则为true
        return stack.empty();
    }

}

 3.栈的压入弹出序列

(1)链接

栈的压入弹出序列icon-default.png?t=N7T8https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&&tqId=11174&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

(2)解析

pushV数组表示压栈后的数组,popV数组表示出栈后的数组可能弹出的顺序

  1. 我们先对pushV数组进行遍历,并将该数组中的元素依次压入栈中,每压一次就与popV数组j下标的元素进行比较,比较之后有两种情况:

    ①若stack中的元素与popV中j下标的元素相同则将栈顶元素进行出栈操作,j++,i++
    (popV数组中必须不能为空,j下标不能超过数组的长度,且只有当栈顶元素与popV数组对应的j下标的元素相等时才会进行出栈操作)

    ②若stack中的元素与popV中j下标的元素不同则i++进行下一次与当前j下标元素的判断(不进入内层循环,直接进行下一次判断)
     
  2. 出栈的过程当中,如果一直是一样的,那么一直出。遇到不一样的。i++继续入栈。
  3. 当循环走完,如果popV数组弹出序列是一致的,那么栈此时应该是空的状态,因此我们只需返回stack.empty()即可

(3)题解

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pushV int整型一维数组 
     * @param popV int整型一维数组 
     * @return bool布尔型
     */
    public boolean IsPopOrder (int[] pushV, int[] popV) {
        // write code here
        Stack<Integer> stack = new Stack<>();
        int j = 0;
        //1.遍历pushv数组
        for(int i = 0; i < pushV.length; i++) {
            stack.push(pushV[i]);
            while(j < popV.length && !stack.empty() && stack.peek() == popV[j]){
                stack.pop();
                j++;
            }
        }
         return stack.empty();
    }
}

4.最小栈

(1)链接

最小栈icon-default.png?t=N7T8https://leetcode.cn/problems/min-stack/

(2)解析

存放元素push的过程:

  1. 如果第一次存放元素,普通栈和最小栈都得存放。
  2. 如果不是第一次存放的时候,普通栈肯定得放,但是最小栈
    我们需要和最小栈的栈顶元素比较,是否比最小栈元素小(小于等于)?只有小了才能放

取元素的过程:pop()

  1. 每次pop元素的时候,都需要判断pop的元素是不是和最小栈的栈顶元素一样?一样:最小栈也得pop.

top ==> peek()返回值是普通栈的值


getMin()获取最小栈的栈顶元素

(3)题解

class MinStack {
    Stack<Integer> stack;
    Stack<Integer> minStack;
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    
    public void push(int val) {
        //不论是第几次入栈,stack都要入栈
        stack.push(val);
        //最小栈第一次入栈时需要放元素,之后的入栈都需要将val与最小栈的栈顶元素进行比较
        if(minStack.empty()) {
            minStack.push(val);
        }else {
            if(val <= minStack.peek()){
                minStack.push(val);
            }
        }
    }
    
    public void pop() {
        if(stack.empty()) {
            return;
        }
        int popval = stack.pop();
        if(minStack.peek() == popval) {
            minStack.pop();
        }
    }
    
    public int top() {
        if(stack.empty()){
            return -1;
        }
        return stack.peek();
    }
    
    public int getMin() {
        if(minStack.empty()){
            return -1;
        }
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1835704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【UIDynamic-动力学-UICollisionBehavior-碰撞行为-创建边界 Objective-C语言】

一、接下来,我们来说一个,碰撞的创建边界, 1.我们刚才呢,仅仅是让self.view,来变成边界, 实际上,这个边界呢,还可以自己去创建, 我们把之前的代码备份一份儿,改个名儿:05-碰撞行为-创建边界, 选中这一段儿,先删掉, command + R, 好,这一段儿,删掉啊, 接下来…

Altair 助力优化摩托车空气动力学性能,实现最佳的整流罩设计

案例简介 整流罩是绝大多数摩托车的重要组成部分&#xff0c;旨在提高车辆的空气动力学性能和稳定性。Altair 与 KTM 公司员工组成的项目团队&#xff0c;针对摩托车整流罩空气动力学方面的学生项目&#xff0c;展开了密切合作。 项目任务主要是对摩托车整流罩设计进行比较&…

基于QT和C++实现的中国象棋

一&#xff0c;源码 board.h #ifndef BOARD_H #define BOARD_H#include <QWidget> #include "Stone.h"class Board : public QWidget {Q_OBJECT public:explicit Board(QWidget *parent 0);bool _bRedTurn; // 红方先走int _currentPlayer; // 当前玩家&…

电子期刊制作秘籍:如何让你的出版物脱颖而出?

​如何让你的电子期刊在众多出版物中脱颖而出&#xff0c;吸引读者的目光呢&#xff1f;在微信公众号这个平台上&#xff0c;让你的电子期刊内容更具吸引力、专业性和创新性&#xff0c;是至关重要的。下面&#xff0c;我将教你制作电子期刊一些方法&#xff0c;助你打造出一本…

在得物的小程序生态实践

一、前言 提起微信小程序&#xff0c;相信所有人都不陌生&#xff0c;下面这个典型使用场景你一定经历过&#xff1a; 餐馆落座——微信扫桌角小程序码——使用微信小程序点餐&#x1f354; 微信小程序&#xff08;下文简称&#xff1a;小程序&#xff09;作为一种在微信平台…

怎么样判断真假单北斗

国产化替代正在中国各行各业逐步提升中&#xff0c;特别涉及重点产业——国家安全&#xff01; 只有仅支持B1I和B3信号的芯片才是真正的单北斗芯片。但凡你支持了B1C、B2a、B2b中的一个就是假的单北斗。 B1C/L1/E1、B2a/ L5/E5a、B2b/G3/E5b这些频点与其他GNSS系统是完全重合的…

举个栗子!Tableau 技巧(277):创建径向 WIFI 信号图

之前为大家分享了 &#x1f330;&#xff1a;学做径向柱状图&#xff08;Radial Column Chart&#xff09;。在此基础上&#xff0c;我们又做了进一步的延展&#xff1a;径向 WIFI 信号图。 它的用法和径向柱状图一致&#xff0c;区别在于它将柱体分切成多个弧线&#xff08;内…

豆包高质量声音有望复现-Seed-TTS

我们介绍了 Seed-TTS&#xff0c;这是一个大规模自回归文本转语音 &#xff08;TTS&#xff09; 模型系列&#xff0c;能够生成与人类语音几乎没有区别的语音。Seed-TTS 作为语音生成的基础模型&#xff0c;在语音上下文学习方面表现出色&#xff0c;在说话人的相似性和自然性方…

pg表空间和mysql表空间的区别

一、表空间的定义 1、在pg中表空间实际上是为表指定一个存储的目录。并且在创建数据库时可以为数据库指定默认的表空间。创建表和索引时可以指定表空间&#xff0c;这样表和索引就可以存储到表空间对应的目录下了。 在pg中一个库中可以有多个表空间&#xff0c;一个表空间可以…

Linux实现: 客户端(cli01)通过TCP(或UDP)连接到聊天服务器(serv)进行聊天?(伪代码版本)

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

6月17(信息差)

1.马斯克最新预测&#xff1a;未来不再需要手机 将被脑机芯片替代 当地时间6月17日&#xff0c;马斯克高仿号“Not Elon Musk”发帖称&#xff1a;“你会在你的大脑上安装一个Neuralink接口&#xff0c;让你通过思考来控制你的新X手机吗&#xff1f;”对此&#xff0c;马斯克本…

东莞酷得:电子玩具嵌入式方案商

东莞市酷得智能科技有限公司&#xff0c;作为一家专业的玩具底层方案服务商&#xff0c;与国内外多家优秀制造企业有着深度合作&#xff0c;始终坚持以孩子为中心&#xff0c;以创新为动力&#xff0c;为孩子们打造独具特色的玩具产品。公司拥有一支专业的设计团队&#xff0c;…

SSM考研咨询app-计算机毕业设计源码05262

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设考研咨询app。 本设计…

大数据------JavaWeb------前端知识点汇总

额外知识点 W3C标准&#xff1a;W3C是万维网联盟&#xff0c;这个组成是用来定义标准的。他们规定了一个网页是由三部分组成 结构&#xff1a;对应的是 HTML 语言表现&#xff1a;对应的是 CSS 语言行为&#xff1a;对应的是 JavaScript 语言 HTML定义页面的整体结构&#xff1…

技术干货:同城O2O系统源码与外卖APP开发

本篇文章&#xff0c;小编将深入探讨同城O2O系统的源码结构&#xff0c;并详细介绍开发一款外卖APP的关键技术和步骤。 一、同城O2O系统概述 一个完整的O2O系统通常包括以下几个模块&#xff1a; 用户端 商家端 配送端 后台管理系统 二、O2O系统源码结构解析 一个完整的…

热重启(硬重启)获取Bitlocker密钥取证

计算机内存&#xff08;条&#xff09;在系统运行时存储了大量敏感信息&#xff0c;当断电后&#xff0c;内存中的数据荡然无存。反之&#xff0c;当一直通电的情况下&#xff0c;内存中的一些敏感数据一直存在。当然&#xff0c;正如某些人了解的&#xff0c;当断电后&#xf…

MES系统功能模块解析,MES系统源码

MES系统功能模块解析&#xff0c;MES系统源码 MES系统是一种用于协调和优化制造过程的信息管理系统&#xff0c;可以帮助企业实现生产计划的顺利执行&#xff0c;并提供全面的生产监控和数据分析功能。 MES系统常见的功能模块包括生产计划管理、物料管理、工艺管理、设备管理…

amr文件怎么转换成mp3?超好用的四种转换方法介绍!

amr文件怎么转换成mp3&#xff1f;在当今数字化时代&#xff0c;音频格式的多样性给我们带来了更广泛的选择&#xff0c;其中AMR格式就是其中之一&#xff0c;AMR格式在录音和通话领域得到广泛应用&#xff0c;但与此同时&#xff0c;它也存在一些挑战和局限性&#xff0c;尽管…

C++ 62 之 冒泡排序

#include <iostream> // #include <string> #include <cstring>using namespace std;// 冒泡排序:函数模板 template<typename T> void mySort(T arr[], int len){ // size参数是数组的个数&#xff0c;一定是int型的for (size_t i 0; i < len -1;…

C++ 61 之 函数模版

#include <iostream> #include <string> using namespace std;void swapInt(int &a,int &b){int temp a;a b;b temp; }void swapDou(double& a, double& b){double temp a;a b;b temp; }// T代表通用数据类型&#xff0c;紧接着后面的代码&a…