3.后来居上的栈

news2024/11/15 4:03:43

概述

目标:

  1. 栈存储结构与特点
  2. 基于数组实现栈
  3. 基于单链表实现栈
  4. 刷题(有效的括号)

存储结构与特点

(Stack)并非指某种特定的数据结构,它是有着相同典型特征的一数据结构的统称,因为栈可以用数组实现,也可以用链表实现,典型的特征是:后进先出Last In First OutLIFO,只要满足这种特点的数据结构,可以说这是栈,为了理解这种数据结构,如下图
在这里插入图片描述
从栈的操作特点上来看,栈就是一种操作受限的线性表,只允许在栈的一端进行数据的插入和删除,这两种操作分别叫做入栈(push)和出栈(pop),时间复杂度均为O(1)

  • 此处说的栈和java语言中的栈空间不是一回事,此处的栈指的是一种数据,而java语言中的栈空间指的是java内存结构的一种表示,不能等同
  • 栈操作虽受限,但当某个数据集合如果只涉及到,在其一端进行数据的插入与删除操作,并且满足先进后出后进先出的特性时,应首选栈这种数据结构来进行数据的存储

栈的实现

栈既可以数组实现,也可以用链表实现,用数组实现的栈叫顺序栈,用链表实现的叫链式栈
功能如下:

  • 返回栈中元素个数
  • 判断栈是否为空
  • 将元素压入栈
  • 获取栈顶元素,但并不移除,如果栈为空,则返回null
  • 移除栈顶元素并返回,如果栈为空,则返回null

数组实现栈

目标:

  1. 基于数组实现一个栈,满足以上定义的方法
  2. 基于数组的栈,要支持动态扩容

代码如下:

package com.example.demo.data;
import java.util.Arrays;

public class Stack<E> {

    // 存储数据的数组
    public E[] elementData;

    // 栈中元素的个数
    public int size;

    /**
     * 指定初始化大小
     *
     * @param initCapacity 初始化值
     */
    public Stack(int initCapacity) {
        this.elementData = (E[]) new Object[initCapacity];
    }

    /**
     * 默认构造 栈大小 10
     */
    public Stack() {
        this(10);
    }

    /**
     * 返回栈中元素个数
     */
    public int size() {
        return this.size;
    }

    /**
     * 判断栈是否为空
     */
    public boolean empty() {
        return this.size == 0;
    }

    /**
     * 将元素压入栈
     */
    public E push(E item) {
        // 压入栈之前判断一下当前的是否还有空间
        ensureCapacity(size + 1);
        // 注意 size++ 后置加法 ,就是此行执行完,才加1
        this.elementData[size++] = item;
        return item;
    }

    /**
     * 获取栈顶元素,但并不移除,如果栈空则返回null
     * 在数组中即最后位置的数据
     */
    public E peek() {
        if (empty()) {
            return null;
        }
        return elementAt(size - 1);
    }

    /**
     * 移除栈顶元素并返回
     */
    public E pop() {
        E peek = peek();
        removeElementAt(size - 1);
        return peek;
    }

    private void removeElementAt(int index) {
        if (index < 0 || index >= this.elementData.length) {
            throw new IndexOutOfBoundsException("下标越界!");
        }
        // 栈中元素个数减一
        size--;
        this.elementData[index] = null;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < this.size; i++) {
            sb.append(this.elementData[i]);
            if (i < this.size - 1) {
                sb.append(",");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    private E elementAt(int index) {
        if (index < 0 || index >= this.size) {
            throw new ArrayIndexOutOfBoundsException("下标越界!");
        }
        return this.elementData[index];
    }

    private void ensureCapacity(int minCapacity) {
        if (minCapacity > this.elementData.length) {
            grow(minCapacity);
        }
    }

    private void grow(int minCapacity) {
        int oldCapacity = this.elementData.length;
        // 增加原来一半的量,要注意 oldCapacity =1 的情况
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity < minCapacity) {
            newCapacity = minCapacity;
        }
        this.elementData = Arrays.copyOf(this.elementData, newCapacity);
    }
}

测试类如下:

public class StackTest {
    public static void main(String[] args) {
        Stack stack = new Stack();
        System.out.println("栈中元素个数:" + stack.size() + ",栈是否为空:" + stack.empty());
        //元素入栈
        stack.push(1);
        stack.push(3);
        stack.push(5);
        stack.push(7);
        System.out.println("栈中元素个数:" + stack.size() + ",栈是否为空:" + stack.empty());
        System.out.println("打印输出栈:" + stack);
        System.out.println("栈顶元素为:" + stack.peek());
        System.out.println("元素出栈" + stack.pop());
        System.out.println("打印输出栈" + stack);
    }

}

链表实现栈

在这里插入图片描述
由上图:因为是单向链表,如查每次元素入栈将其添加到链表尾的话(相当于将链表尾当栈顶),后面在进行出栈要删除栈顶元素时,没办法找到它的前一个元素,因此将链表头节点当作栈顶

public class LinkedListStack<E> {

    // 栈中元素个数
    int size;
    // 栈顶指针,链表头结点指针
    Node<E> head;
    
    /**
     * 返回栈中元素个数
     */
    public int size() {
        return this.size;
    }

    /**
     * 判断栈是否为空
     */
    public boolean empty() {
        return this.size == 0;
    }

    public E push(E item) {
        Node<E> node = new Node<>(item, head);
        head = node;
        size++;
        return item;
    }
    public E peek(){
        if (head == null) {
            return null;
        }
        return head.val;
    }

    public E pop() {
        if (head == null) {
            return null;
        }
        Node<E> top = head;
        head = head.next;
        top.next = null;
        return top.val;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node cur = head;
        while (cur != null) {
            sb.append(cur.val).append("->");
            cur = cur.next;
        }
        sb.append("null");
        return sb.toString();
    }

    /**
     * 定义单链表节点类
     *
     * @param <E>
     */
    private static class Node<E> {
        E val;
        Node<E> next;

        public Node(E val, Node<E> next) {
            this.val = val;
            this.next = next;
        }
    }
}

测试:

数组的测试案例,拿过来改 LinkedListStack stack = new LinkedListStack(); 即可进行测试

在这里插入图片描述

刷题(有效的括号)

有效的括号

步骤: 栈先入后出特点恰好与括号排序特点一致,即若遇到左括号就将对应的右括号入栈,遇到右括号时将对应栈顶元素出栈,出栈的元素与当前右括号相同,遍历完所有括号后 stack 仍然为空;

若括号字符串如:s = "()[]{}"

  1. 第一次( 则入栈一个 )
  2. 第二次是) 则将上一次入栈的 ) 弹出(上一次正好在栈顶),正好值相等
  3. 若第二次是( ,则会再入栈 ),此时栈中就有两个)
  4. 正常匹配的括号,最终栈会清空

测试代码如下

public class Bracket {

    public static void main(String[] args) {
        String s = "(]";
        System.out.println(isValid(s));;
    }

    public static boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        char[] chars = s.toCharArray();
        if (chars.length % 2 != 0) {
            // 奇数位肯定不符合
            return false;
        }
        for (char c : chars) {
            // 遇到左括号就将对应的右括号入栈,否则就弹出栈顶元素
            if (c == '(') {
                stack.push(')');
            } else if (c == '[') {
                stack.push(']');
            } else if (c == '{') {
                stack.push('}');
            } else if (stack.isEmpty() || c != stack.pop()) {
                return false;
            }
        }
        return stack.isEmpty();
    }
}

总结

java 中对于栈这种数据结构已经有了对应的实现,如下图
在这里插入图片描述

  • ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问,它适合随机查找和遍历,不适合插入和删除
  • VectorArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费
  • LinkedList是用链表结构(双向链表)存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。
  • VectorStack是线程(Thread)同步(Synchronized)的,所以它也是线程安全的,而ArrayList是线程异步(ASynchronized)的,是不安全的
  • Stack是继承自Vector,底层也是基于数组实现的,只不过Stack插入和获取元素有一定的特点,满足后进先出的特点即LIFO,因此Stack也是典型的“栈”这种数据结构,且底层也支持动态扩容,其扩容方式和VectorArrayList底层扩容原理一样。Stack元素入栈和出栈的时间复杂度都是O(1)

结束

至此就结束了,如有问题,欢迎评论区提出

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

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

相关文章

超详细的万字Git分支教程(保姆级别)

&#x1f497;推荐阅读文章&#x1f497; &#x1f338;JavaSE系列&#x1f338;&#x1f449;1️⃣《JavaSE系列教程》&#x1f33a;MySQL系列&#x1f33a;&#x1f449;2️⃣《MySQL系列教程》&#x1f340;JavaWeb系列&#x1f340;&#x1f449;3️⃣《JavaWeb系列教程》…

stable-diffusion的模型简介和下载使用

前言 我们下载完stable-diffusion-ui后还需要下载需要的大模型&#xff0c;才能进行AI绘画的操作。秋叶的stable-diffusion-ui整合包内&#xff0c;包含了anything-v5-PrtRE.safetensors和Stable Diffusion-V1.5-final-prune_v1.0.ckpt两个模型。 anything-v5-PrtRE.safetenso…

PowerShell系列(十三):PowerShell Cmdlet高级参数介绍(三)

目录 1、WarningAction参数 2、WarningVariable 出现警告后的变量 3、Whatif 假设参数 4、Confirm参数 今天给大家讲解PowerShell Cmdlet高级参数第三部分相关的知识&#xff0c;希望对大家学习PowerShell能有所帮助&#xff01; 1、WarningAction参数 通过单词含义&…

IG-Net:一种用于地铁客流预测的交互图网络模型

文章信息 论文题目为《IG-Net: An Interaction Graph Network Model for Metro Passenger Flow Forecasting》的一篇2023年4月发表在IEEE TRANSACTIONS ON INTELLIGENT TRANSPORTATION SYSTEMS的地铁客流预测交互图网络模型。 摘要 城市轨道交通系统很大程度上满足了市民的出行…

C++标准模板(STL)- 类型支持 (复合类型类别,is_scalar,is_object)

类型特性 类型特性定义一个编译时基于模板的结构&#xff0c;以查询或修改类型的属性。 试图特化定义于 <type_traits> 头文件的模板导致未定义行为&#xff0c;除了 std::common_type 可依照其所描述特化。 定义于<type_traits>头文件的模板可以用不完整类型实例…

后门分析及示例

代码分析&#xff0c;关键字定位 传一个asp文件 输入账户错误会提示&#xff1a;非法登录&#xff1b; 逆向工程抓取这个关键字定位 查找代码里面的关键字&#xff0c;定位到关键字后把代码复制出来&#xff0c; 修改exec执行函数为msgbox消息弹出用gb2312方式保存成VBS文件.…

Leetcode刷题详解——不同路径 II

1. 题目链接&#xff1a;63. 不同路径 II 2. 题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finis…

企业链表(未完成)

文章目录 1. 插入2. 代码实现 1. 插入 // 插入 void insert(LinkList* list, int pos, LinkNode* data) {if (!list || !data)return;if (pos < 0 || pos > list->size)return;LinkNode* curNode &(list->head);for (int i 0; i < pos; i) {curNode curN…

jenkins工具系列 —— 删除Jenkins JOB后清理workspace

文章目录 问题现象分析解决思路脚本实现问题现象分析 Jenkins使用过程中,占用空间最大的两个位置: 1 、workspace: 工作空间,可以随便删除,删除后再次构建时间可能会比较长,因为要重新获取一些资源。 2 、job: 存放的是项目的配置、构建结果、日志等。不建议手动删除,…

任正非说:浑水摸鱼,只有强者才能摸到鱼。

嗨&#xff0c;你好&#xff01;这是华研荟【任正非说】系列的第24篇文章&#xff0c;让我们聆听任正非先生的真知灼见&#xff0c;学习华为的管理思想和管理理念。 一、只强调精细化管理&#xff0c;公司是会萎缩的&#xff0c;精细化管理的目的&#xff0c;是为了扩张不陷入混…

最新版上门服务小程序源码 同城技师上门服务系统源码

最新版上门服务小程序源码 同城技师上门服务系统源码 需要了解的请看文末 系统介绍&#xff1a; 1、数据概况&#xff08;新增业务城市用户投票功能&#xff0c;更加直观的查看业务城市的关注度、人气和影响力,促进业务开展&#xff09; 2、数据概况 &#xff08;增加可视化…

【Linux命令】Linux常见命令介绍(最强超详细版本)

Linux常见命令 1. Linux 常用命令1.1 用户配置 2.目录及文件操作3. 文件查看及处理命令3. 其他命令 1. Linux 常用命令 1.1 用户配置 Linux 下有两种用户&#xff1a;超级用户&#xff08; root&#xff09; &#xff09; 、普通用户。 a) 超级用户&#xff1a;可以再 linux …

Linux开发者的利器:深入了解环境开发工具之yum篇

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; &#x1f354;前言&#xff1a;在博主的博客中&#xff0c;Linux系统我们已经将关键指令、权限等等全部了解完了。接下来我们应该学习什么呢&#xff1f;当我们拿起一个手机或电脑&#xff0c;我们最先想到的就是下载QQ、…

什么是Webpack?它的主要功能是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

内网渗透-域信息收集

域环境 虚拟机应用&#xff1a;vmware17 域控主机&#xff1a;win2008 2r 域成员主机&#xff1a;win2008 2r win7 一.域用户和本地用户区别 使用本地用户安装程序时&#xff0c;可以直接安装 使用域用户安装程序时&#xff0c;需要输入域控管理员的账号密码才能安装。总结…

【Proteus仿真】【STM32单片机】便携式恒温箱设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用报警模块、LCD1602显示模块、DS18B20温度模块、加热制冷模块、按键模块、HC05蓝牙模块等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1…

最新Ai智能创作系统源码V3.0,AI绘画系统/支持GPT联网提问/支持Prompt应用+搭建部署教程

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

HDRI贴图下载及Three.js利用

最令人兴奋的项目之一是在 Three js 中添加HDRI背景。 HDRI图像是从房间内部或花园、丛林或山脉等开放环境等场景中以 360 度捕获的。 你可以自己创建任何这些图像&#xff0c;但这不是本教程的主题。 相反&#xff0c;我们将从网站获取这些图像之一&#xff0c;并使用轨道控件…

(免费领源码)小程序+spring boot+mysql疫起买菜平台99212-计算机毕业设计项目选题推荐

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;疫起买菜平台小程序被用户普遍使用&#xff0c;为方便用户…

Transformer.js简明教程【Web AI】

Transformers.js 可在你的 Web 浏览器中实现最先进的机器学习&#xff0c;无需服务器。 它提供预训练模型和熟悉的 API&#xff0c;支持自然语言处理、计算机视觉、音频和多模态领域的任务。 借助 Transformers.js&#xff0c;开发人员可以直接在浏览器中运行文本分类、图像分类…