【数据结构】栈的使用|模拟实现|应用|栈与虚拟机栈和栈帧的区别

news2024/12/21 19:19:51

目录

一、栈(Stack)

1.1 概念

1.2 栈的使用

1.3 栈的模拟实现

1.4 栈的应用场景

1. 改变元素的序列

2. 将递归转化为循环

3. 括号匹配

4. 逆波兰表达式求值

5. 出栈入栈次序匹配

6. 最小栈

1.5 概念区分


一、栈(Stack)

1.1 概念

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

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

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


1.2 栈的使用

方法功能
Stack()构造一个空的栈
E push(E e)将e入栈,并返回e
E pop()将栈顶元素出栈并返回(有返回值)
E peek()获取栈顶元素
int size()获取栈中有效元素个数
boolean empty()检测栈是否为空
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是线程安全的

栈的模拟实现有两种:一种是数组实现,另一种是链表(单链表或者双链表)实现,不管是哪种,都得保证入栈 出栈操作的时间复杂度为O(1)

下面这个是数组模拟实现栈的方式:

import java.util.Arrays;

//数组实现栈
public class MyStack {

    public int[] elem;//定义数组
    public int uesdSize;//记录当前数组的有效元素的个数,同时可以当作下标使用

    public static final int DEFAULT_CAPACITY = 10;//默认容量大小

    public MyStack() {
        this.elem = new int[DEFAULT_CAPACITY];
    }

    //判断栈是否满了
    public boolean isFull() {
        return uesdSize == elem.length;//这里不能写成DEFAULT_CAPACITY,DEFAULT_CAPACITY被final修饰不能变
    }
    //压栈 入栈
    public void push(int val) {
        if (isFull()) {
            this.elem = Arrays.copyOf(elem,2*elem.length);//扩容为原数组
        } else {
            elem[uesdSize++] = val;
        }
    }
    //判空
    public boolean isEmpty() {
        return uesdSize == 0;
    }
    //出栈
    public int pop() {
        if (isEmpty()) {
            throw new EmptyStackException("栈为空...");
        }
        int oldVal = elem[uesdSize-1];
        uesdSize--;
        elem[uesdSize] = 0;
        return oldVal;
    }
    //获取栈顶元素
    public int peek() {
        if (isEmpty()) {
            throw new EmptyStackException("栈为空...");
        }
        return elem[uesdSize-1];
    }
}

如果采用单向链表实现栈,那么为了保证入栈出栈的时间复杂度为O(1)

入栈只能采用头插法,尾插法需要遍历链表直到尾结点,这样就不满足时间复杂度为O(1)

出栈也只能采用头删法,可能大家会想用last来标记尾结点,从而不用遍历,但是这样在删除了一次以后,尾节点还得去遍历找前一个结点,还是不满足时间复杂度为O(1)

如果采用双向链表实现栈,那么头插尾插都是可以的


1.4 栈的应用场景

1. 改变元素的序列

1. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()

A: 1,4,3,2         B: 2,3,4,1         C: 3,1,4,2         D: 3,4,2,1

2.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是( )。

A: 12345ABCDE  B: EDCBA54321  C: ABCDE12345  D: 54321EDCBA

答:

1.由于栈的特性是先进后出,C选项中:当1,2,3都已经入栈后,3出栈,然后栈顶为2,不可能直接就让1进行出栈,所以错误

2.仍然考察的是栈的特性是先进后出,先进栈的元素最后出栈,那么也就是B

2. 将递归转化为循环

比如:逆序打印链表

// 递归方式
    void printList(Node head){
        if(null != head){
            printList(head.next);
        System.out.print(head.val + " ");
        }
    }

这里循环的方式就类似上面的第二题,入栈元素出栈也就相当于逆序 

// 循环方式
    void printList(Node head){
        if(null == head){
            return;
        }
    Stack<Node> s = new Stack<>();
        // 将链表中的结点保存在栈中
        Node cur = head;
        while(null != cur){
            s.push(cur);
            cur = cur.next;
        }
        // 将栈中的元素出栈
        while(!s.empty()){
            System.out.print(s.pop().val + " ");
        }
    }
3. 括号匹配

首先思考一下为什么这个题需要用到栈这个数据结构,什么时候会用到这个数据结构?

一般和顺序有关的就需要考虑栈

这题的思路:

首先要明白这个题目不是偶数就一定是匹配的,eg:[( ] )

 只要是左括号就入栈,遇到右括号就看是否匹配

以下三种情况是不匹配的:

(1)右括号不匹配 就直接返回false 

(2)字符串还没遍历完成 但是栈是空的 此时也是不匹配  eg:())

(3)字符串遍历完了 但是栈不为空 此时也是不匹配  eg:()(

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 == '(') {
                //左括号入栈
                stack.push(ch);
            } else {
                //右括号
                if(stack.isEmpty()) {
                    //栈为空
                    return false;
                } 
                //栈不为空,右括号判断匹配
                 char ch2 = stack.peek();
                 if(ch2=='{'&&ch=='}'||ch2=='['&&ch==']'||ch2=='('&&ch==')') {
                 stack.pop();
                 } else {
                    return false;
                 }
            }
        }
        //遍历完了,但是栈不为空
        if(!stack.isEmpty()) 
            return false;
        return true;

         //return stcak.isEmpty() 可以直接代替前三行
    }
}

注意

(1) Stack<Character> stack = new Stack<>();这里的类型为Character

  (2) ch2为左括号,ch为右括号

(3)怎么判断匹配,一组一组符合即可


4. 逆波兰表达式求值

看这题之前,我们先来学习一下什么是前中后缀表达式,中缀表达式 转 后缀表达式 ,最后再来看怎么根据后缀表达式计算结果

(1)中缀表达式

        操作符以中缀形式位于运算数中间(如:3+2),是我们日常通用的算术和逻辑公式表示方法

(2)后缀表达式

        又称逆波兰式(Reverse Polish Notation - RPN),操作符以后缀形式位于两个运算数后(如:3+2的后缀表达形式就是3 2 +)

(3)前缀表达式:

        又称波兰式(Polish Notation),操作符以前缀形式位于两个运算数前(如:3+2的前缀表达形式就是+ 3 2)

手工 如何将中缀表达式 转 后缀表达式?

以a+b*c+(d*e+f)*g为例,将其转为 后缀表达式

(1)按先加减后乘除的原则给表达式加括号,得到的就是 (a+(b*c)+( ( (d*e)+f )*g

(2)由内到外把括号去掉,并把运算符放在要去掉括号的后面,也就是 abc*+  de*f+ g* +

计算器的逻辑就是这样的,会把我们输入的带有括号的表达式转为不带括号的表达式,因为计算器也不知道括号是啥

 在这里代码题考的最多的就是根据后缀表达式计算结果,那么思路是什么呢?

将后缀表达式中的数字依次入栈,   遇到运算数,就弹出栈顶的两个元素

第一个数字作为右操作数,第二个数作为左操作数,然后把 数字2  运算数 数字1 计算得到的结果入栈 (这个顺序不能改变)

然后继续这个过程,直到栈中只剩下最后一个元素,直接返回即可

代码实现:

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for(int i=0;i<tokens.length;i++) {
            String str = tokens[i];
           if(!isOperatons(str)) {
               //不是运算符,也就是数字
               //将字符串转为数字
               int val = Integer.valueOf(str);
               //将数字入栈
               stack.push(val);
           } else {
               //是运算符
                  //弹除两个栈顶元素,第一个为右操作数
               int num2 = stack.pop();
               int num1 = stack.pop();
                  //计算
               switch(str) {
                   case "+":
                        stack.push(num1+num2);
                        break;
                    case "-":
                        stack.push(num1-num2);
                        break; 
                   case "*":
                        stack.push(num1*num2);
                        break; 
                   case "/":
                        stack.push(num1/num2);
                        break;
               }
           }
        }
        return stack.pop();
    }
    //判断这个字符串是不是一个运算符
    private boolean isOperatons(String str) {
        if(str.equals("+")||str.equals("-")||str.equals("*")||str.equals("/")) {
            return true;
        } else {
            return false;
        }
    }
}

注意:

(1)入栈需要把字符串变为数字  int val = Integer.valueOf(str);

(2)弹除两个栈顶元素,第一个为右操作数,第二个为左操作数

5. 出栈入栈次序匹配

 思路:

(1)遍历pushV数组 ,把pushV数组的元素放到栈当中

(2)每次放一个元素,就得看和popV的元素是否一样

(3)如果不一样,i++    一样的话,j++,并将栈顶元素弹出(i是遍历pushA数组的,j是遍历popA数组的)

直到 遍历完popV 结束

如下图 当pushV栈顶元素和popV[j]一样,我们是需要将pushA的栈顶元素出栈的,不然无法判断下一个元素是否相等

public class Solution {
    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]);
            //如果pushV栈顶元素和popV[j]一样
            while(!stack.isEmpty()&&j<popV.length&&stack.peek()==popV[j]) {
                j++;
                stack.pop();
            }
        }
        if(j<popV.length) {
            return false;
        }
        return true;

        //return j == popV.length;  //这里行可以代替前三行
        //return stack.isEmpty;   //或者这样写也行
    }
}

注意:当pushV栈顶元素和popV[j]一样时,可能存在 j下标越界 , 栈被弹空了的情况,所以需要特别考虑

6. 最小栈

 思路:

这题一个栈无法得到最小元素的(如果最小元素不在栈顶,那么时间复杂度就不满足O(1),违背了题目条件),那么就考虑用两个栈

(1)普通栈Stack用来存储数据 , 最小栈minStack用来存最小元素

(2)普通栈一定要存有元素

(3)最小栈 如果是第一次存放数据 直接放 ,否则需要和最小栈的栈顶元素去比较 <=的时候才存入(这里特别注意一下=的时候,看图解释:这个时候如果右边的-3不放,当普通栈pop,最小栈也pop,那么最小值就不会是-3,而是-2,这显然不符合)

class MinStack {
    Stack<Integer> stack;
    Stack<Integer> minStack;
    //构造方法:初始化两个栈
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    
    public void push(int val) {
        stack.push(val);
        //如果第一次放(也就是minStack为空),直接放即可
        if(minStack.isEmpty()) {
            minStack.push(val);
        } else {
            //不是第一次放,那就只有val<= minStack栈顶元素才可以放
            if(val<= minStack.peek()) {
                minStack.push(val);
            }
        }
    }
    
    public void pop() {
        //根据题目不用考虑空栈
           int val = stack.pop();
           //如果普通栈pop出的元素就是最小,那么minStack也需要pop
           if(minStack.peek()==val) 
           {
               minStack.pop();
           }
        
        
    }
    //获取栈顶元素
    public int top() {
        return stack.peek();
    }
    //获取最小值
    public int getMin() {
        return minStack.peek();
    }
}

1.5 概念区分

栈、虚拟机栈、栈帧有什么区别呢?

:一种先进后出的数据结构

虚拟机栈:JVM的一块内存

栈帧:调用方法时,给方法开辟的一块内存


本次内容就到此啦,欢迎评论区或者私信交流,觉得笔者写的还可以,或者自己有些许收获的,麻烦铁汁们动动小手,给俺来个一键三连,万分感谢 !

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

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

相关文章

数据库——水果商店进阶

智能2112杨阳 一、目的与要求&#xff1a; 综合运用SQL语言相关知识如变量、游标、函数、触发器等解决实际问题。 二、内容&#xff1a; 设计并完成以下实验&#xff0c;要求附上源码&#xff08;非截图&#xff09;&#xff0c;测试效果截图 在订单详情表orderitems插入新…

【openGauss/MogDB列存表的delta表测试】

列存储格式是OLAP类数据库系统最常用的数据格式&#xff0c;适合复杂查询、范围统计类查询的在线分析型处理系统。cstore列存储的主体数据文件以CU为I/O单元&#xff0c;只支持追加写操作&#xff0c;因此cstore只有读共享缓冲区。CU间和CU内的可见性由对应的CUDESE表&#xff…

高项-【整合管理】

8.1管理基础 项目整合管理由项目经理负责&#xff0c;责任不能被授权和转移。对整个项目承担最终责任。执行项目整合是担任双重角色&#xff1a; 组织层面&#xff0c;与项目发起人携手合作&#xff0c;了解战略目标&#xff0c;确保项目目标和成果与项目组合、项目集一集业务…

自助借还办证一体机软件需求说明书

1. 简介 1.1 项目概括 本项目主要实现读者自助办证、借书、还书、查询、续借的功能&#xff0c;减轻管理员的工作量&#xff0c;提升读者的借阅体验&#xff0c;提高了图书的借阅量与流通率&#xff0c;是图书馆智能化、无人化建设的重要步骤。 1.2 项目背景 ​ 目前各大图…

【ArkTS】如何修改应用的首页

之前看到一种说法&#xff0c;说是应用首页是 entry > src > main > resources > base > profile > main_pages.json 中src配置中数组第一个路径元素。这种说法是不对的&#xff01;&#xff01;&#xff01; 如果需要修改应用加载时的首页&#xff0c;需要…

软件测试面试题之测试基础,轻松面对面试,一篇足矣

软件测试的流程是什么&#xff1f;&#xff08;测试流程&#xff09; &#xff08;1&#xff09;需求调查&#xff1a;全面了解系统概况、应用领域、软件开发周期、软件开发环境、开发组织、时间安排、功能需求、性能需求、质量需求及测试要求等。根据系统概况进行项目所需的人…

oracle与gbase8s迁移数据类型对照

声明&#xff1a;以下为笔者阅读gbase官方文档和oracle官方文档的理解&#xff0c;如有错误&#xff0c;敬请指正。oracle与gbase8s迁移数据类型对照及举例说明 最终结论&#xff1a;oracle与gbase8s数据类型对应关系关于单精度与双精度的区别关于定点与浮点定义的区别精度的定…

linux之Samba服务器

环境&#xff1a;虚拟机CENTOS 7和 测试机相通 一、Samba服务器_光盘共享&#xff08;匿名访问&#xff09; 1.在虚拟机CENTOS 7安装smb服务&#xff0c;并在防火墙上允许samba流量通过 2. 挂载光盘 3.修改smb.conf配置文件&#xff0c;实现光盘匿名共享 4. 启动smb服务 5.在…

Bezier 曲线 2D

Bezier 曲线于 1962 年由法国雪铁龙汽车公司的工程师 Bezier 所发表&#xff0c;主要应用于汽车的外形设计。虽然 Bezier 曲线早在 1959 年便由法国雷诺汽车公司的 De Casteljau 运用递推算法开发成功&#xff0c;但是 Bezier 却给出了曲线的详细的曲线计算公式。所以&#xff…

游戏、算法竞赛与退役(流水账版)

写在前面 不出意外的话&#xff0c;这东西本该咕到翻年之后再发的&#xff0c;但好像催稿催的有点厉害&#xff0c;于是就找个机会把他写了&#xff08;笑&#xff09; 最初是只想写个算法竞赛退役记的&#xff0c;后面发觉写起来就有点收不住&#xff0c;算法竞赛牵扯到太多…

linux网络管理_配置网络参数

11.2 配置网络参数 ls /etc/sysconfig/network-scripts/ 11.2.1 配置IP 配置网卡参数 # 可考虑先备份 # cp /etc/sysconfig/network-scripts/ifcfg-ens33 . # 复制到当前目录 ​ vim /etc/sysconfig/network-scripts/ifcfg-ens33 ifcfg-ens33文件中的内容 TYPEEthernet PROX…

MATLAB 平面拟合并可视化(34)

MATLAB 平面拟合并可视化(34) 一、效果二、代码一、效果 二、代码 % 生成三维点数据 x = rand(100, 1); y = rand(100, 1

LLaMA系列模型

1.LLama 1.1 简介 Open and Efficient Foundation Language Models (Open但没完全Open的LLaMA) 2023年2月&#xff0c;Meta&#xff08;原Facebook&#xff09;推出了LLaMA大模型&#xff0c;使用了1.4T token进行训练&#xff0c;虽然最大模型只有65B&#xff0c;但在相关评…

Python实战:信用卡客户历史数据挖掘与分析

Python实战&#xff1a;信用卡客户历史数据挖掘与分析 引言数据获取与预处理描述性分析模型建立与评估结果分析Web应用展示&#xff08;可选&#xff09; 引言 信用卡客户历史数据分析是金融领域中的重要课题之一。通过对公开数据集的挖掘&#xff0c;本文将利用Python编程语言…

51单片机LED与无源蜂鸣器模块

IO口的使用1 本文主要对51单片机的LED灯的使用以及蜂鸣器的使用进行介绍&#xff0c;其中包括一些实例分析&#xff1a; 1.实现发光二极管的从左到右的流水点亮 2.左右来回循环的流水灯 3.蜂鸣器以一定频率响 文章目录 IO口的使用1一、LED灯举个栗子一举个栗子二 二、蜂鸣器2.1…

华为OD机试 - 连续出牌数量 - 深度优先搜索dfs算法(Java 2023 B卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明 四、解题思路1、题目解读2、具体步骤 五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08…

Linux--学习记录(3)

G重要编译参数 -g&#xff08;GDB调试&#xff09; -g选项告诉gcc产生能被GNU调试器GDB使用的调试信息&#xff0c;以调试程序编译带调试信息的可执行文件g -g hello.c -o hello编译过程&#xff1a; -E&#xff08;预处理&#xff09; g -E hello.c -o hello.i-S&#xff08;编…

基于springboot+vue 的智能物流管理系统

简介 基于springbootvue 的智能物流管理系统 适用于 设计&#xff0c;课程设计参考与学习用途。仅供学习参考。 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料 **项目编号&#xff1a;springboot074 ** **…

C++刷题 -- KMP算法

C刷题 – KMP算法 文章目录 C刷题 -- KMP算法1.算法讲解2.算法实现 https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/ 1.算法讲解 KMP算法是一种字符串匹配算法&#xff0c;当出现字符串不匹配时&#xff0c;可以记录一部分之…

数据可视化---箱线图

类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统计学检验箱…