数据结构——栈的实现(java实现)与相应的oj题

news2024/12/27 16:29:35

文章目录

  • 一 栈
    • 栈的概念:
    • 栈的实现:
    • 栈的数组实现
        • 默认构造方法
        • 压栈
        • 获取栈元素的个数
        • 出栈
        • 获取栈顶元素
        • 判断当前栈是否为空
    • java提供的Stack类
      • Stack实现的接口:
    • LinkedList也可以当Stack使用
    • 虚拟机栈,栈帧,栈的三个概念
  • 二 栈的一些算法题:
    • (1) 逆序打印单链表。
    • (2)[括号匹配](https://leetcode-cn.com/problems/valid-parentheses)
    • (3)[逆波兰表达式求值](https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/)
      • 逆波兰表达式
    • (4) [出入栈次序匹配](https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&&tqId=11174&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking)
    • (5)[最小栈](https://leetcode-cn.com/problems/min-stack/)
    • 总结:


一 栈

栈的概念:

栈也是一种线性表,栈只允许在表的一端进行插入与删除操作,所以栈中数据的特征的先进后出(先进来的后出去)。
栈顶:是栈进行插入与删除操作的表的一端
栈底:不允许插入删除操作的一端
压栈:将数据插入到栈中
出栈:将数据移除栈
在这里插入图片描述

栈的实现:

栈的底层可以由数组实现,也可以由链表实现
栈由数组实现时,栈的压栈与出栈的时间复杂度均为O(1)。
栈由单链表实现时,
如果采用尾插法,压栈操作时间复杂度为O(n),如果有last指针,则
压栈时间复杂度为O(1),但是出栈的时间复杂度一定为O(n)。
如果采用头插法,则压栈与出栈的时间复杂度均为O(1).
栈由双链表实现时,不论采用头插法与尾插法,时间复杂度均为O(1)。

栈的数组实现

当栈使用数组实现时,栈的插入与删除操作,时间复杂度均为O(1);

public class MyStack {
    static   int  DEFAULT_SIZE  = 10; //设置默认的数组大小
    private   int [] element ;        //实现栈的数组
    int usedsize = 0 ;                //栈中有效元素的个数
	//默认构造方法
	public MyStack() {}
	//入栈
    public void push(int val) {}
	//出栈
    public int pop() {}
    //获取栈顶元素 但是不删除
    public int peek() { }
     //获取栈元素个数
    public int size(){} 
    //判空
    public boolean isEmpty() { }       
}
默认构造方法

默认申请10个空间

public MyStack(){
        this.element = new int[10];
    }
压栈

思想:先判断栈满没满,如果满了则扩容,然后进行压栈

  public void push(int data){
        //先判断栈是否有空间
        if(usedsize==element.length){
            //如果没有空间
            //则扩容
            this.element = Arrays.copyOf(element,2*element.length);
        }
        //保证空间后
       this.element[usedsize++] = data;
  }
获取栈元素的个数

思想:直接返回usedsize

 public int size(){
        return usedsize;
    }
出栈

思想:先判断栈是否为空,为空则抛出异常,不为空,则返回栈顶元素值,并且usedsize-1.

  public int pop(){
        //要判断栈是否为空,如果为空,则退出
      try{
          if (isEmpty()) {
              //2. 问题出现,构造参数的形参问题,super()调用父类的构造方法
              throw new EmptyException("EmptyException异常报错");
          }
      }catch (EmptyException e){
              e.printStackTrace();
      }
        int val =   this.element[usedsize-1];
        usedsize -- ;
        return val;
    }
获取栈顶元素

思想:与出栈逻辑相同,只是usedsize值不需-1 。

 public int  peek(){
        try{
            if (isEmpty()) {
                throw new EmptyException("EmptyException异常报错");
            }
        }catch (EmptyException e){
            e.printStackTrace();
        }
        return    this.element[usedsize-1];
    }
判断当前栈是否为空

思想:直接判断usedsize 是否为0即可。

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

java提供的Stack类

在这里插入图片描述

Stack实现的接口:

在这里插入图片描述

  接口说明   1.  继承Cloneable接口支持克隆
  其他接口,暂时还没搞明白,以后更新补充

LinkedList也可以当Stack使用

java提供的LinkedList类也可以当做栈来使用,LinkedList的方法列表如下:
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        LinkedList<Integer> stack =  new LinkedList<>();
        stack.push(5);
        stack.push(2);
        stack.pop();
        stack.pop();
        stack.pop();
    }
}

在这里插入图片描述

虚拟机栈,栈帧,栈的三个概念

虚拟机栈是JVM中的一块内存。
栈帧是为一个方法,函数分配的内存。
栈是一种数据结构。

二 栈的一些算法题:

(1) 逆序打印单链表。

采用递归的方法:

// 递归方式
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 + " ");
   }
}

(2)括号匹配

题解:1 . 对于这个题无法直接看出其数学模型,必须先画图,列出各种情况,再判断
在这里插入图片描述
从图中可以分析出,要实现判断括号匹配

  1. 就要使得每一个左括号和右括号能够与对应位置的右括号进行匹配判断
  2. 且当所有的左括号(右括号)判断完时,其对应的右括号(左括号)也判断完毕,即满足左括号个数与右括号个数相当的情况。
  3. 如果第一个括号是右括号,说明此括号没有对应的左括号,说明一定为字符串一定括号不匹配。

实现思想:遍历字符串,遇到左括号则存入栈中,遇到右括号则与栈顶元素比较,相匹配,则继续循环,如果不匹配或栈为空,则返回false。

class Solution1 {
    public boolean isValid(String s) {
        Stack <Character> stack = new Stack<>();
       //采用for循环,这样可以找到字符串中的字符
        for(int i =0;i<s.length();i++) {
            char ch = s.charAt(i);
            if (ch == '{' || ch == '[' || ch == '(') {
                //问题,java提供的栈是否需要手动扩容?
                stack.push(ch);
            } else {
                //如果是右括号的内容
                if (stack.isEmpty()) {
                    return false;
                } else {
                    char ch2 = stack.peek();   //当获取栈顶元素时,栈可能为空
                    //栈区不能为空
                    if (((ch == '}' && ch2 == '{') || (ch == ']' && ch2 == '[') || (ch == ')' && ch2 == '('))) {
                        //说明匹配,
                        //将栈顶的元素去除
                        stack.pop();
                    } else {
                        //如果不匹配,情况1 如果栈中还没有左括号,则一定不匹配,
                        return false;
                    }
                }
            }
        }
         // 如果栈表为空,说明匹配完毕
        if(stack.isEmpty()){
            return true;
        }
       return false;
    }
}

(3)逆波兰表达式求值

逆波兰表达式

逆波兰表达式即后缀表达式,后缀表达式是由中缀表达式转换而成。
所谓中缀表达式即我们平常所写的±*/的算式
我们平常所写的中缀表达式中是有优先级的,即先乘除,后加减,有括号先算括号里面的
但是计算机是不能够识别优先级的,为了能够使计算机计算,我们将中缀表达式的规则
表达成后缀表达式的形式,这样计算机便能够计算算式。

中缀表达式转换成后缀表达式的的规则:

  1. 先将中缀表达式中能够加括号的部分都加上括号
  2. 然后将所有的运算符移到所在 最近扩号之外。
  3. 然后去除掉所有括号即得到后缀表达式。

后缀表达式的运算规则:
遍历整个表达式,遇到运算符时,则将运算符前面的两个数作为操作数计算
得出的结果替代两个操作数,然后继续遍历表达式,循环上次操作。

在这里插入图片描述
本题:只是要求按照后缀表达式的规则计算结果
实现思想:遍历表达式,遇到操作数即将操作数压入栈中,遇到运算符,则进行两次出栈
,获取两个操作数进行计算(需要注意的是第一次出栈的是右操作数,第二次出栈的是左操作数)
然后将计算的结果再进行压栈,然后循环此操作。

class Solution {
    public int evalRPN(String[] tokens) {
        //创建一个栈,创建栈时不需要考虑栈的空间大小
        Stack<Integer> stack = new Stack<>();
        //采用foreach进行遍历栈
        for (String str : tokens) {
            if (!isOperator(str)) {
                int a = Integer.parseInt(str);
                stack.push(a);
            } else {
                int val2 = stack.pop();
                int val1 = stack.pop();
                //如果为运算符
                switch (str) {
                    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.peek();
    }

总结
1. 字符串形式的运算符不能直接转换成运算符
2. 后缀表达式先进栈的是左操作数,后进栈的是右操作数,当我们需要出栈时,第一次获取栈顶元素是右操作数,第二次获取栈顶元素才是左操作数。

(4) 出入栈次序匹配

在这里插入图片描述

class Solution2{
    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++;
            }
        }

        return stack.empty();
    }
}

(5)最小栈

题解:
如果只用一个栈的话,我们不可能实现题目的要求,只有能将栈的最小值时刻储存起来才能实现题目的要求。因此我们实现两个栈,一个栈用于存储数据,另一个最小栈用于存放栈当前的最小值。

在这里插入图片描述实现思想:入栈的规则:

  1. 普通栈一定要放,最小栈如果为空,或者栈顶元素小于压入的数据,则放入最小栈中
  2. 如果压入的数据与最小栈栈顶元素相当,也放入最小栈中。(因为最小栈中的元素必须与普通栈的最小值保持同步即必须与出栈的规则相匹配)。
    出栈的规则:
    如果普通栈的栈顶元素与最小栈栈顶元素相同,则最小栈也需要出栈。
class MinStack {
   //创建两个栈
   Stack <Integer> stack  =  new Stack<>(); //普通栈
   Stack <Integer> minStack = new Stack<>();//最小栈
    public MinStack() {
    }
public void push(int val) {
         //压栈,压入数据
         //普通栈一定要压入
         stack.push(val);
         //然后判断最小栈是否压入
         if(minStack.empty()){
            minStack.push(val);
         }else{
            if(val<=minStack.peek()){
                 minStack.push(val);
            }
         }
  }
  public void pop() {
          //出栈
          //普通栈一定出栈
             int val =  stack.pop();
          //最小栈出栈:
          //如果两栈的栈顶元素相同,则出栈
          if(val == minStack.peek()){
            minStack.pop();
          }
    }
    public int top() {
            //获取栈顶元素
            if(!stack.empty()){
                   return stack.peek();
            }
            return -1 ;
    }
    public int getMin() {
           if(minStack.empty()){
            return -1;
           }
           return minStack.peek();
    }
    }

总结:

在使用栈时,最常用的运用是根据栈先进后出的特性,将一段有序的数据转为逆序后,再进行操作使用。

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

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

相关文章

Android 11 HAL层集成FFMPEG

1.集成目录&#xff1a; android/vendor/noch/common/external/NoboMediaCodec 2.文件夹目录 3. Android.mk实现 # Copyright #LOCAL_PATH : $(call my-dir)SF_COMMON_MK : $(LOCAL_PATH)/common.mkinclude $(call first-makefiles-under,$(LOCAL_PATH))4.common.mk实现 # #…

Xilinx FPGA DDR4 接口配置基础(PG150)

1. 简介 1.1 DDR4 SDRAM 控制器主要特点 支持8到80位接口宽度的组件&#xff08;支持 RDIMM、LRDIMM、UDIMM 和 SODIMM&#xff09; 最大组件限制为9&#xff0c;此限制仅适用于组件&#xff0c;不适用于 DIMM。密度支持 最高支持 32 GB 的组件密度&#xff0c;64 GB 的 LRDI…

初识godot游戏引擎并安装

简介 Godot是一款自由开源、由社区驱动的2D和3D游戏引擎。游戏开发虽复杂&#xff0c;却蕴含一定的通用规律&#xff0c;正是为了简化这些通用化的工作&#xff0c;游戏引擎应运而生。Godot引擎作为一款功能丰富的跨平台游戏引擎&#xff0c;通过统一的界面支持创建2D和3D游戏。…

数字集成电路(3)

光刻&#xff08;photolithography&#xff09; 工艺步骤&#xff1a; 扩散和离子注入&#xff1a;900~1100℃ 淀积 刻蚀 平面化 衬底选择&#xff1a;常用&#xff08;100&#xff09;晶面&#xff08;原因&#xff1a;面密度小&#xff0c;界面态少&#xff09; 设计规…

【vue教程】四. Vue 计算属性和侦听器

目录 本章涵盖知识点回顾计算属性&#xff08;Computed&#xff09;创建计算属性计算属性的多样性计算属性的数组过滤计算属性的复杂表达式 计算属性 vs 方法计算属性的实例演示 侦听器&#xff08;Watchers&#xff09;创建侦听器侦听器的高级用法侦听器的深度观察侦听器的立即…

【ffmpeg命令基础】过滤处理

文章目录 前言过滤处理的介绍两种过滤类型简单滤波图简单滤波图是什么简单滤波示例 复杂滤波图复杂滤波是什么区别示例 总结 前言 FFmpeg是一款功能强大的开源音视频处理工具&#xff0c;广泛应用于音视频的采集、编解码、转码、流化、过滤和播放等领域。1本文将重点介绍FFmpe…

mysql存储引擎和备份

索引 事务 存储引擎 概念&#xff1a;存储引擎&#xff0c;就是一种数据库存储数据的机制&#xff0c;索引的技巧&#xff0c;锁定水平。 存储引擎。存储的方式和存储的格式。 存储引擎也属于mysql当中的组件&#xff0c;实际上操作的&#xff0c;执行的就是数据的读写I/O。…

ROC曲线和AUC

ROC曲线能更稳定反映模型的性能&#xff0c;对测试集合中数据分布的变化不敏感 AUC&#xff1a;当随机挑选一个正样本和一个负样本&#xff0c;根据当前的分类器计算得到的score将这个正样本排在负样本前面的概率 从AUC判断分类器&#xff08;预测模型&#xff09;优劣的标准&a…

【QT开发(19)】2023-QT 5.14.2实现Android开发,使用新版SDK,试图支持 emulator -avd 虚拟机

之前的博客【QT开发&#xff08;17&#xff09;】2023-QT 5.14.2实现Android开发&#xff0c;SDK是24.x版本的&#xff0c;虚拟机是32位的&#xff0c;但是现在虚拟机是64位的了&#xff0c;需要升级SDK匹配虚拟机 文章目录 最后的效果1.1 下载最新版 SDK tools (仅限命令行工…

JavaWeb-【3】DOM

笔记系列持续更新&#xff0c;真正做到详细&#xff01;&#xff01;本次系列重点讲解后端&#xff0c;那么第一阶段先讲解前端【续上篇CSS和JavaScript】 目录 1、dom介绍 2、html-dom 3、document 4、应用实例 ①、应用实例1 ②、多选框案例 ③、图片切换案例 ④、添…

高性能图数据库Neo4j从入门到实战

图数据库Neo4j介绍 什么是图数据库&#xff08;graph database&#xff09; 随着社交、电商、金融、零售、物联网等行业的快速发展&#xff0c;现实社会织起了了一张庞大而复杂的关系网&#xff0c;传统数据库很难处理关系运算。大数据行业需要处理的数据之间的关系随数据量呈…

密码学基础-Hash、MAC、HMAC 的区别与联系

密码学基础-Hash、MAC、HMAC 的区别与联系 Hash Hash 是一种从一段数据中创建小的数字“指纹”的方法。就像一个人的指纹代表一个人的信息一样&#xff0c;Hash 对输入的数据进行整理&#xff0c;生成一个代表该输入数据的“指纹” 数据。通常该指纹数据也可称之为摘要、散列…

CefSharp音视频编译与免费下载

注&#xff1a;Cefharp 音频和视频播放编译&#xff0c;生成相应的dll文件&#xff0c;从而支持项目开发。 建议编译至少 16G 的 RAM和至少 250G 的 SSD。该脚本以 E 盘为例&#xff0c;您需要在 E 盘上手动创建 cef 文件夹。禁止在转载后通过发布其他平台向用户收取下载费用。…

全国区块链职业技能大赛第八套区块链产品需求分析与方案设计

任务1-1:区块链产品需求分析与方案设计 医疗健康平台中涉及到医院、医生、患者等参与方,他们需要在区块链医疗健康平台中完成账户注册、身份上链、挂号就诊、查询病例等多种业务活动。通过对业务活动的功能分析,可以更好的服务系统的开发流程。基于医疗健康平台系统架构,以…

【数据结构进阶】二叉搜索树

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; C || 数据结构 目录 &#x1f308;前言&#x1f525;二叉搜索树&#x1f525; 二叉搜索树的实现Insert&#xff08;插入&#xff09;find&#xff08;查找&#xff09;erase(删除)destro…

毕业/期刊论文发表必备:YOLOv5 / v7 / v8 /v10算法网络结构图【文末提供原型文件下载地址】

前言:Hello大家好,我是小哥谈。同学们在写YOLO算法相关毕业论文/期刊论文的时候,不可避免的会用到相关版本的网络结构图,曾有很多小伙伴私信我索要原型文件,本文就给大家提供YOLOv5/v7/v8/v10版本算法网络结构图及原型文件下载地址。🌈 目录 🚀1.网络结构图 �…

Fiddler 导出请求为curl格式

来自:https://www.cnblogs.com/yudongdong/p/15418181.html Fiddler 下载地址: https://downloads.getfiddler.com/fiddler-classic/FiddlerSetup.5.0.20243.10853-latest.exe 这段代码加到类中 public static RulesOption("关闭请求体转代码", "生成代码&qu…

简单页表和多级页表

地址转换(Address Translation) 内存需要被分成固定大小的页(Page)然后再通过虚拟内存地址(Virtual Address) 到物理内存地址(Physical Address) 的地址转换(Address Translation)才能到达实际存放数据的物理内存位置 简单页表 页表的概念 想要把虚拟内存地址&#xff0c;映…

ip地址是电脑还是网线决定的

在数字化时代的浪潮中&#xff0c;网络已经成为了我们日常生活和工作不可或缺的一部分。当我们谈论网络时&#xff0c;IP地址无疑是一个核心的概念。然而&#xff0c;关于IP地址的分配和决定因素&#xff0c;很多人可能存在误解。有些人认为IP地址是由电脑决定的&#xff0c;而…

pytorch 46 将ASpanFormer模型导出onnx运行

ASpanFormer是一个2022年8月份发布的算法,其主要步骤与LoFTR模型类似,因此无法导出为onnx模型。根据ASpanFormer论文中的数据与效果图,可以确定AsPanFormer是可以作为一个比SP+SG更为有效的方案,其在标准数据集上的效果优于SP+SG,在速度上远超SP+SG,与LoFTR接近;在预测点…