一文带你了解栈的基本概念以及栈的实现

news2024/11/18 0:33:06

✏️✏️✏️今天给大家分享一下栈的基本概念、线性栈的自定义实现,以及栈的应用题目。

清风的CSDN博客

😛😛😛希望我的文章能对你有所帮助,有不足的地方还请各位看官多多指教,大家一起学习交流!

动动你们发财的小手,点点关注点点赞!在此谢过啦!哈哈哈!😛😛😛

目录

一、关于栈(Stack)

1.1 栈的概念

1.2 栈的使用

 1.3 栈的模拟实现

1.3.1 栈的类定义

1.3.2 判断栈空或栈满

1.3.3 出栈

1.3.4 入栈

1.3.5 获取栈顶元素

1.3.6 获取栈中当前元素个数

二、栈的应用

2.1 后缀表达式求值

2.2 括号匹配

 2.3 最小栈

 2.4 栈的压入、弹出序列


一、关于栈(Stack)

1.1 栈的概念

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

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

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

 栈在现实生活中的例子:

 上面的两个例子都遵循后进先出的原则。

1.2 栈的使用

栈可以在库函数中直接调用,比如下面的代码:

public static void main(String[] args) {
Stack<Integer> s = new Stack();
     s.push(1);
     s.push(2);
     s.push(3);
     s.push(4);
     System.out.println(s.size()); // 获取栈中有效元素个数---> 4
     System.out.println(s.peek()); // 获取栈顶元素---> 4
     s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
     System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
     if(s.empty()){
     System.out.println("栈空");
     }else{
         System.out.println(s.size());
     }
}

 1.3 栈的模拟实现

从上图中可以看到, Stack 继承了 Vector Vector ArrayList 类似,都是动态的顺序表,不同的是 Vector 是线程安全的。

 所以,我们就可以利用顺序表来实现栈。

1.3.1 栈的类定义

默认栈的大小为10,也可以通过构造函数自己定义栈的大小。

public class MyStack {
    private final int DEFALUT_CAPACITY=10;
    private int[] elem;
    private int usedSize;
    public MyStack(){
        elem=new int[DEFALUT_CAPACITY];
    }

    public MyStack(int size){
        int[] elem=new int[size];
    }
}

1.3.2 判断栈空或栈满

栈空:在栈的类定义中,我们定义了一个usedSize来表示当前栈中的元素个数,因此,判断栈是否为空,只需要判断usedSize是否为0即可。

 public boolean isEmpty(){
        return usedSize==0;
    }

栈满:如果当前栈中的元素个数和数组的长度相等,那么就判栈满。

  public boolean isFull(){
        return elem.length==usedSize;
    }

1.3.3 出栈

数组的最后一个元素便是栈顶的元素,返回这个元素即可。

    public int pop(){
        if(isEmpty()){
            System.out.println("栈空!");
            return -1;
            //或者抛自定义的异常
        }
        int old=elem[usedSize-1];
        usedSize--;
        //若是引用类型:>elem[usedSize]=null;
        return old;
    }

1.3.4 入栈

    public void push(int data){
        if(isFull()){
            elem= Arrays.copyOf(elem,elem.length*2);
        }
        elem[usedSize]=data;
        usedSize++;
    }

1.3.5 获取栈顶元素

    public int peak(){
        if(isEmpty()){
            System.out.println("栈空!");
            return -1;
        }
        return elem[usedSize-1];//获取栈顶元素
    }

1.3.6 获取栈中当前元素个数

  public int size(){
        return usedSize;
    }

二、栈的应用

2.1 后缀表达式求值

后缀表达式求值的基本思路是:>当遇到的字符串是数字时,把它压入栈中,而当遇到的字符串是操作符时,从栈中弹出两个元素做对应的运算,再把运算结果压入栈中。字符串遍历完成后,栈顶元素就是计算的结果。这里需要注意,当遇到操作符要执行出栈操作是,第一次出栈的元素是计算时的右操作数,第二次出栈的元素是计算时的左操作数。

比如下面的题目:

给你一个字符串数组 tokens ,表示一个根据后缀表达式表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

 根据上面的思路,我们写这个代码其实就非常简单了:>

      public int evalRPN(String[] tokens) {
        Stack<Integer> stack=new Stack<>();
        for (String x:tokens) {
            if(!isOperation(x)){
                //如果不是操作符,就把x转为数字并压栈
                stack.push(Integer.parseInt(x));
            }else{
                //弹出两个操作数,并做相应的运算
                int num2=stack.pop();
                int num1=stack.pop();
                switch (x){
                    case "+":
                        stack.push(num1+num2);
                        break;
                    case "-":
                        stack.push(num1-num2);
                        break;
                    case "*":
                        stack.push(num1*num2);
                        break;
                    case "/":
                        stack.push(num1/num2);
                        break;
                }
                
            }
        }
        int ret = stack.pop();
        return ret;
    }
    private boolean isOperation(String s){
        if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")){
            return true;
        }
        return false;
    }

2.2 括号匹配

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 每个右括号都有一个对应的相同类型的左括号。

思路:>

  • 遍历字符串,当遇到这三种左括号时,全部压入栈中。
  •  当遇到右括号时,如果当前栈空,直接返回false,因为这种情况是不可能匹配成功的。
  • 如果当前栈不空,先获取(不能直接出栈)栈顶元素,与当前的右括号进行匹配。
  • 若匹配成功,当前与之匹配的栈顶元素出栈,继续向后遍历。
  • 否则匹配不成功,返回false。
  • 当遍历完成后,只需判断当前栈是否为空,若为空,那肯定是匹配成功。
  • 若遍历完成后,当前栈非空,说明匹配失败,返回false。(说明左括号多)

下面是代码实现:>

       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=='{')){
                stack.push(ch);
            }else{
                if(!stack.empty()){
                    //如果栈不空
                    char ch2=stack.peek();//ch是右括号,ch2是左括号
                    if((ch2=='(' && ch==')') || (ch2=='{' && ch=='}') || (ch2=='[' && ch==']')){
                        //左括号出栈
                        stack.pop();
                    }else {
                        return false;
                    }
                }else {
                    return false;
                }
            }
        }
        if(!stack.empty()){
            //i已经遍历完成,栈还不为空,返回false
            return false;
        }
        return true;
    }

 2.3 最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。(这里指普通栈)
  • int top() 获取堆栈顶部的元素。(这里指普通栈)
  • int getMin() 获取堆栈中的最小元素。

思路:>

利用两个栈来同时进行相关的操作,需要定义一个普通栈(Stack),还需要定义一个存放最小元素的栈(MinStack)。

关于入栈:>

  • 普通栈无论如何是要进行入栈操作的,那么只需要考虑最小栈是否要进行入栈操作。
  • 最小栈存放的是最小元素,所以每次普通栈进行入栈的时候,需要把当前要进入普通栈的元素(val)和在最小栈里的栈顶元素进行比较,如果val小于等于最小栈中的栈顶元素,此时最小栈也是需要执行入栈操作的。
  • 需要注意的是,在普通栈进行第一次入栈操作的时候,最小栈也是需要入栈的,也就是说,当最小栈当前为空,直接入栈即可。若最小栈非空,才需要比较大小,让小的压入最小栈。

关于出栈:> 

  •  执行出栈操作时,为了确保在获取最小元素的时候不出错,同样需要把当前从普通栈弹出的元素和最小栈中的栈顶元素比较(因为要确保最小栈存放的是当前栈的最小值)。
  • 如果普通栈中弹出的元素比最小栈中的栈顶元素大,那么普通栈弹出元素并不会影响获取当前栈中的最小元素,直接出栈即可。
  • 当普通栈中弹出元素等于(不可能小于)最小栈的栈顶元素时,这两个栈要同时执行出栈操作。(因为如果此时最小栈不弹出,并不能更新普通栈弹出元素后,此时普通栈的最小值)

下面的具体的代码实现:>

class MinStack {
    private Stack<Integer> stack;
    private 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{
            int peekVal=MinStack.peek();
            if(val<=peekVal){
                MinStack.push(val);
            }
        }
    }
    
    public void pop() {
        /**
         * pop的时候和stack的栈顶元素比较,如果相等,全部出栈
         * 不一样,只出普通栈
         */
        int val=stack.pop();
        if(!MinStack.empty()){
            if(val==MinStack.peek()){
                MinStack.pop();
            }
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        if(!MinStack.empty()){
            return MinStack.peek();
        }
        return -1;
    }
}

 2.4 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

  •  0<=pushV.length == popV.length <=1000
  • -1000<=pushV[i]<=1000 
  • pushV 的所有数字均不相同

 思路:>

  • 遍历入栈数组,同时遍历给定的弹出序列。
  • 每次将入栈数组中的元素入栈后,就和给定的弹出序列比较。
  • 若相等,那么直接将入栈的元素弹出。
  • 遍历结束后,若栈空,说明给定的序列可以成为该栈的弹出序列。否则,返回false。 

 下面是具体的实现代码:>

    public boolean IsPopOrder (int[] pushV, int[] popV) {
        // write code here
        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++;
            }
        }
        if (stack.empty()) {
            return true;
        }
        return false;
    }

希望各位看官读完文章后,能够有所提升。

🎉好啦,今天的分享就到这里!!

✨创作不易,还希望各位大佬支持一下!

👍点赞,你的认可是我创作的动力!

⭐收藏,你的青睐是我努力的方向!

✏️评论:你的意见是我进步的财富!

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

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

相关文章

13. 高精度延时

13. 高精度延时 GPT 定时器简介GPT 定时器结构GPT 定时器工作模式 GPT 定时器相关寄存器GPTx_CRGPTx_PRGPTx_SRGPTx_CNTGPTx_OCR GPT 配置步骤程序编写bsp_delay.hbsp_delay.cmain GPT 定时器简介 GPT 定时器是一个 32 位向上定时器&#xff0c;也就是从0x00000000 开始向上递…

蓝桥杯算法竞赛第一周题型总结

本专栏内容为&#xff1a;蓝桥杯学习专栏&#xff0c;用于记录蓝桥杯的学习经验分享与总结。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &#x1f339;&#x1f33…

LCD1602设计(1)

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

【技术支持】DevTools中重写覆盖源js文件

sources面板下&#xff0c;左侧overrides标签下添加一个文件夹&#xff0c;并同意。 勾选Enable Local overrides 然后在page标签下&#xff0c;修改文件后ctrls保存 直接就保存在overrides的文件夹下了

FL Studio 21.2.0.3842中文破解版2024最新系统要求

FL Studio 21.2.0.3842中文版完整下载是最好的音乐开发和制作软件也称为水果循环。它是最受欢迎的工作室&#xff0c;因为它包含了一个主要的听觉工作场所。2024最新fl studioFL Studio 21版有不同的功能&#xff0c;如它包含图形和音乐音序器&#xff0c;帮助您使完美的配乐在…

颠覆人工智能计算硬件的新计算技术

颠覆人工智能计算硬件的新计算技术 图纸解释说明参考网址加法器模拟解析图纸 解释说明 简单的介绍 使用一个小的llm 模拟 计算最小单元加法器 等硬件 在使用 简单的 电阻矩阵模拟矩阵计算 固化llm 参数代替 半导体硬件 而后组成 大规模人工智能计算 参考网址 加法器 但是直接…

CMake教程--QT项目使用CMake

CMake教程--QT项目使用CMake Chapter1 CMake教程--QT项目使用CMake1. Basic Cmake Based Project2. Executable VS Library3. Every module has its own CMakeList.txt in its folder3.1 不推荐的做法&#xff1a;3.2 推荐的做法 4. 强制以Debug, Release, RelWithDebInfo, Min…

2024“点点点”测试员如何上岸测试开发岗?附完整学习路线

有很多人员会不断问自己&#xff0c;自己到底要不要学测试&#xff0c;或者要不要坚持做测试&#xff0c;测试的职业发展到底怎么样&#xff1f;如果你还在迷茫&#xff0c;在到处找各种大牛问类似的问题&#xff0c;我希望这篇文章&#xff0c;你看完能够结束你的这个烦恼&…

论文笔记:SimiDTR: Deep Trajectory Recovery with Enhanced Trajectory Similarity

DASFFA 2023 1 intro 1.1 背景 由于设备和环境的限制&#xff08;设备故障&#xff0c;信号缺失&#xff09;&#xff0c;许多轨迹以低采样率记录&#xff0c;或者存在缺失的位置&#xff0c;称为不完整轨迹 恢复不完整轨迹的缺失空间-时间点并降低它们的不确定性是非常重要…

Python进行多维数据分析

多维数据分析是对数据的信息分析&#xff0c;它考虑了许多关系。让我们来介绍一些使用Python分析多维/多变量数据的基本技术。 从这里找到用于说明的数据的链接。&#xff08;https://archive.ics.uci.edu/dataset/111/zoo&#xff09; 以下代码用于从zoo_data. csv读取2D表格…

无人机航迹规划:五种最新智能优化算法(GRO、SWO、COA、LSO、KOA)求解无人机路径规划MATLAB

一、五种算法&#xff08;GRO、SWO、COA、LSO、KOA&#xff09;简介 1、淘金优化算法GRO 淘金优化算法&#xff08;Gold rush optimizer&#xff0c;GRO&#xff09;由Kamran Zolf于2023年提出&#xff0c;其灵感来自淘金热&#xff0c;模拟淘金者进行黄金勘探行为。VRPTW&am…

2015年计网408

第33题 通过 POP3 协议接收邮件时, 使用的传输层服务类型是( ) A. 无连接不可靠的数据传输服务 B. 无连接可靠的数据传输服务 C. 有连接不可靠的数据传输服务 D. 有连接可靠的数据传输服务 本题考察邮件接收协议POP3使用的运输层服务类型。 如图所示。接收方用户代理使用pop…

Maven内网开发使用离线仓库

Maven内网开发使用离线仓库 离线或者内网环境开发与外网不通&#xff0c;中央仓库连不上&#xff0c;使用 Maven 管理项目会遇到很多问题。 比如&#xff1a;依赖包缺失&#xff0c;内网的Nexus私服的包老旧&#xff0c;很久没有维护&#xff0c;项目无法运行打包&#xff0c;…

pytorch中对nn.BatchNorm2d()函数的理解

pytorch中对BatchNorm2d函数的理解 简介计算3. Pytorch的nn.BatchNorm2d()函数4 代码示例 简介 机器学习中&#xff0c;进行模型训练之前&#xff0c;需对数据做归一化处理&#xff0c;使其分布一致。在深度神经网络训练过程中&#xff0c;通常一次训练是一个batch&#xff0c…

Dart笔记:一些代码生成工具站点的介绍

Dart笔记&#xff1a; 一些代码生成工具站点的介绍 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/1343…

Easyui DataGrid combobox联动下拉框内容

发票信息下拉框联动&#xff0c;更具不同的发票类型&#xff0c;显示不同的税率 专票 普票 下拉框选择事件 function onSelectType(rec){//选中值if (rec2){//普通发票对应税率pmsPlanList.pmsInvoiceTaxRatepmsPlanList.pmsInvoiceTaxRateT}else {//专用发票对应税率pmsPlan…

置换环算法

参考该博客大佬的讲解 置换环 - TTS-S - 博客园 (cnblogs.com) 置换环&#xff1a;一般用于解决数组排序元素间所需最小交换次数这类问题。 置换环思想&#xff1a;置换环是将每个元素指向其应在的位置&#xff0c;最终相连成一个环(若元素就在其应在的位置&#xff0c;则自身…

js 根据当前时间往前推15天或往后推15天等(例如当前时间往后15天后的日期。并实现今天、明天、后天、周)

本次分享&#xff0c;在项目中开发车票购买功能需要用到日期筛选 思路&#xff1a; 1、首先获取当前时间戳 2、根据当前时间戳拿到15天后的日期 3、根据天匹配星期几 4、将时间戳转换年、月、日并重组 实现代码 // 获取当前日期 const today new Date();// 往前推15天的…

【数据结构】树与二叉树(七):二叉树的遍历(先序、中序、后序及其C语言实现)

文章目录 5.2.1 二叉树二叉树性质引理5.1&#xff1a;二叉树中层数为i的结点至多有 2 i 2^i 2i个&#xff0c;其中 i ≥ 0 i \geq 0 i≥0。引理5.2&#xff1a;高度为k的二叉树中至多有 2 k 1 − 1 2^{k1}-1 2k1−1个结点&#xff0c;其中 k ≥ 0 k \geq 0 k≥0。引理5.3&…

Interactive Analysis of CNN Robustness

Interactive Analysis of CNN Robustness----《CNN鲁棒性的交互分析》 摘要 虽然卷积神经网络&#xff08;CNN&#xff09;作为图像相关任务的最先进模型被广泛采用&#xff0c;但它们的预测往往对小的输入扰动高度敏感&#xff0c;而人类视觉对此具有鲁棒性。本文介绍了 Pert…