Java数据结构栈和队列(Stack和Queue详解)

news2024/10/8 13:14:31

前言:

        栈和队列是数据结构中重要的存储方式,也是建立在Arrarylist和LinkedList之上的数据结构。

        本猿在C语言阶段就已经详细剖析过栈和队列,不熟悉的小伙伴可以翻看之前的博客内容!

      Java阶段一方面剖析Java中自带的Stack和Queue中的各种方法,另一方面也是重点重新再次实现栈和队列,  并发掘和C语言中的区别。

栈(Stack):

        栈是一种"先进后出"的数据结构,如图:

Java中的栈:

        在Java的util包中有Stack类,我们可以看到Stack的源代码:

继承了Vector,那么Vector中又有哪些继承关系呢?

我们发现继承了AbstractList,并且扩展了好多接口,其中包括List接口。

综上我们可以以一张结构图概括:

栈的模拟实现:

        我们利用顺序表模拟实现栈,当然也可以用链表实现栈,Java中用的是顺序表!至于为什么要用顺序表,等自己实现一遍栈大概就清楚了!

    

public class MyStack {
    private int[] array;
    private int size;
    private static final int DEFFAULEVALUE = 10;
    public MyStack() {
        array = new int[DEFFAULEVALUE];
    }
    public void push(int value) {
    ensureCapacity();
    array[this.size++] = value;
    }
    public int pop(){
        if(empty()) {
            throw new RuntimeException("栈为空,无法获取栈顶元素");
        }
        size--;
        return array[size];
    }
    public int peek(){
        if(empty()){
            throw new RuntimeException("栈为空,无法获取栈顶元素");
        }
        return array[size-1];
    }
    public int size(){
        return size;
    }
    
    private boolean empty() {
        if(this.size == 0) {
            return true;
        }
        return false;
    }
    private void ensureCapacity() {
        if(this.size == array.length) {
            array = Arrays.copyOf(array,size*2);
        }
    }
}

注意:

        在main方法中测试的时候,需要注意打印栈当中的元素的时候需要pop打印,也就是需要用到for循环打印,但是这样写对不对呢?

        

for (int i = 0; i < myStack.size(); i++) {
    System.out.println(myStack.pop());
}

这样的for循环是有问题的,问题在于每次pop结束之后,size()的值在发生变化,也就是这个循环不会执行size()次。

        所以以上遍历会出现问题,应该这样遍历:

int num = myStack.size();
for (int i = 0; i < num; i++) {
    System.out.println(myStack.pop());
}

这样遍历才不会出现问题!! 

关于栈的习题(典型):

1、括号匹配问题:

        20. 有效的括号 - 力扣(LeetCode)

        具体题目请参考力扣,具体实现代码如下:

        

class Solution {
    public boolean isValid(String s) {
        if(s == null) {
            return true;
        }
        Stack<Character> stack = new Stack<>();
        for(int i = 0;i<s.length();i++) {
            char a = s.charAt(i);
            if(stack.empty()) {
                if(a == ')' || a == ']' || a == '}') {
                    return false;
                }
            }
            if(a == '(' || a == '{' ||a == '[') {
                stack.push(a);
            }else {
                char b = stack.pop();
                if(!(a == ')' && b == '(' || a == ']' && b == '[' || a == '}' && b == '{')) {
                    return false;
                }
            }
        }
        if(!stack.empty()) {
            return false;
        }
        return true;
    }
}

2、逆波兰表达式求值:

     150. 逆波兰表达式求值 - 力扣(LeetCode)   

class Solution {
       public int evalRPN(String[] tokens) {
        Stack<String> str = new Stack<>();
        for(int i = 0;i < tokens.length;i++) {
            if(tokens[i].equals("+")) {
                int num1 = Integer.valueOf(str.pop());
                int num2 = Integer.valueOf(str.pop());
                str.push(num1+num2+"");
            }else if(tokens[i].equals("-")) {
                int num1 = Integer.valueOf(str.pop());
                int num2 = Integer.valueOf(str.pop());
                str.push(num2-num1+"");
            }else if(tokens[i].equals("*")) {
                int num1 = Integer.valueOf(str.pop());
                int num2 = Integer.valueOf(str.pop());
                str.push(num1*num2+"");
            }else if(tokens[i].equals("/")) {
                int num1 = Integer.valueOf(str.pop());
                int num2 = Integer.valueOf(str.pop());
                str.push(num2/num1+"");
            }
            else {
                str.push(tokens[i]);
            }

        }
        return Integer.valueOf(str.pop());
    }
}

3、出栈进栈次序匹配:

栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)

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

4、最小栈:

155. 最小栈 - 力扣(LeetCode)

class MinStack {
    Stack<Integer> st1;
    Stack<Integer> st2;
    int minInteger;
    public MinStack() {
        st1 = new Stack<>();
        st2 = new Stack<>();
    }

    public void push(int val) {
        if(st1.empty() && st2.empty()) {
            minInteger = val;
            st1.push(val);
            st2.push(val);
        }else {
            st1.push(val);
            if(val < minInteger) {
                st2.push(val);
                minInteger = val;
            }else {
                st2.push(minInteger);
            }
        }
    }

    public void pop() {
        st1.pop();
        st2.pop();
        if(!st2.empty()) {
        minInteger = st2.peek();
        }
    }

    public int top() {
        return st1.peek();
    }

    public int getMin() {
        return st2.peek();
    }
}

队列(Queue):

队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为 队尾( Tail/Rear 出队列:进行删除操作的一端称为 队头 Head/Front ).

Java中的队列:

Java 中, Queue 是个接口,底层是通过链表实现 的。

也是一系列的继承关系。

队列的模拟实现:

public class MyQueue {
    // 双向链表节点
    public static class ListNode{
        ListNode next;
        ListNode prev;
        int value;
        ListNode(int value){
            this.value = value;
        }
    }
    ListNode first; // 队头
    ListNode last; // 队尾
    int size = 0;
    // 入队列---向双向链表位置插入新节点
    public void offer(int e){
        ListNode newNode = new ListNode(e);
        if(first == null){
            first = newNode;
// last = newNode;
        }else{
            last.next = newNode;
            newNode.prev = last;
// last = newNode;
        }
        last = newNode;
        size++;
    }
    // 出队列---将双向链表第一个节点删除掉
    public int poll(){
// 1. 队列为空
// 2. 队列中只有一个元素----链表中只有一个节点---直接删除
// 3. 队列中有多个元素---链表中有多个节点----将第一个节点删除
        int value = 0;
        if(first == null){
            return -1;
        }else if(first == last){
            last = null;
            first = null;
        }else{
            value = first.value;
            first = first.next;
            first.prev.next = null;
            first.prev = null;
        }
        --size;
        return value;
    }
    // 获取队头元素---获取链表中第一个节点的值域
    public int peek(){
        if(first == null){
            return 0;
        }
        return first.value;
    }
    public int size() {
        return size;
    }
    public boolean isEmpty(){
        return first == null;
    }
}

循环队列:

        实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。 环形队列通常使用数组实现。
数组下标循环的小技巧
1. 下标最后再往后 (offset 小于 array.length): index = (index + offset) % array.length
2. 下标最前再往前 (offset 小于 array.length): index = (index + array.length - offset) % array.length

如何区分空与满:

1. 通过添加 size 属性记录
2. 保留一个位置
3. 使用标记
接下来我们就来实战一下:
622. 设计循环队列 - 力扣(LeetCode)
要求:
分别用三种不同的区分空和满的方式:
方式一:
class MyCircularQueue {
    private int[] arr;
    private int size;
    private int rear;
    private int front;
    public MyCircularQueue(int k) {
        arr = new int[k];
        size = 0;
    }
    
    public boolean enQueue(int value) {
        if(isFull()) {
            return false;
        }
        arr[rear] = value;
        size++;
        rear = (rear+1)%arr.length;
        return true;
    }
    
    public boolean deQueue() {
        if(isEmpty()) {
            return false;
        }
        front = (front+1)%arr.length;
        size--;
        return true;
    }
    
    public int Front() {
        if(isEmpty()) {
            return -1;
        }
        return arr[front];
    }
    
    public int Rear() {
        if(isEmpty()) {
            return -1;
        }
        return arr[(rear+arr.length-1)%arr.length];
    }
    
    public boolean isEmpty() {
        if(size == 0) {
            return true;
        }
        return false;
    }
    
    public boolean isFull() {
        if(size == arr.length) {
            return true;
        }
        return false;
    }
}

方式二:

class MyCircularQueue {
    private int[] arr;
    private int rear;
    private int front;
    public MyCircularQueue(int k) {
        arr = new int[k+1];
        rear = 0;
        front = 0;
    }
    
    public boolean enQueue(int value) {
        if(isFull()) {
            return false;
        }
        arr[rear] = value;
        rear = (rear+1)%arr.length;
        return true;
    }
    
    public boolean deQueue() {
        if(isEmpty()) {
            return false;
        }
        front = (front+1)%arr.length;
        return true;
    }
    
    public int Front() {
        if(isEmpty()) {
            return -1;
        }
        return arr[front];
    }
    
    public int Rear() {
        if(isEmpty()) {
            return -1;
        }
        return arr[(rear+arr.length-1)%arr.length];
    }
    
    public boolean isEmpty() {
        if(rear == front) {
            return true;
        }
        return false;
    }
    
    public boolean isFull() {
        if((rear+1)%arr.length == front) {
            return true;
        }
        return false;
    }
}

方式三:大家可以自己试试!

双端队列:

        双端队列(deque )是指允许两端都可以进行入队和出队操作的队列, deque “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
        此时该场景应该有几种组合:
        对头出,对头进。
        对头出,对尾进。
        对尾出,对头进。
        队尾出,对头出。
        也就是该双端队列既可以当作栈也可以当作队列!!
        Deque 是一个接口,使用时必须创建 LinkedList 的对象。
Deque<Integer> stack = new ArrayDeque<>();// 双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();// 双端队列的链式实现

 

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

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

相关文章

宠物咖啡馆平台开发:SpringBoot框架的高效应用

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

【Spring】“请求“ 之传递 JSON 数据

文章目录 JSON 概念JSON 语法JSON 的语法JSON 的两种结构 JSON 字符串和 Java 对象互转JSON 优点传递 JSON 对象 JSON 概念 JSON&#xff1a;JavaScript Object Notation【JavaScript 对象表示法】 JSON 就是一种数据格式&#xff0c;有自己的格式和语法&#xff0c;使用文本…

什么是PLM系统?PLM系统对制造业起到哪些作用?三品PLM系统对汽车制造业意义

在当今竞争激烈的制造业环境中&#xff0c;企业面临着来自市场、技术、客户需求等多方面的挑战。为了应对这些挑战&#xff0c;许多制造企业纷纷引入产品生命周期管理PLM系统&#xff0c;以实现更高效、更灵活的产品全生命周期管理。PLM系统以其独特的优势&#xff0c;在优化产…

社区圈子系统 圈子社区系统 兴趣社区圈子论坛系统 圈子系统源码圈子系统的适用领域有哪些?如何打造自己的圈子圈子系统有哪些常见问题

社区圈子系统 圈子社区系统 兴趣社区圈子论坛系统 圈子系统源码圈子系统的适用领域有哪些&#xff1f;如何打造自己的圈子圈子系统有哪些常见问题 圈子系统的适用领域 圈子系统的适用领域广泛&#xff0c;涵盖了多个行业和场景&#xff0c;包括但不限于以下几个方面&#xff1…

计算机毕业设计 自习室座位预约系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

火语言发布暨火车采集器VIP用户回馈活动

火车头软件系列产品从最初的火车采集器、火车浏览器到触控精灵一直致力于数据采集领域的深耕与拓展&#xff0c;不断优化用户体验&#xff0c;力求为用户提供更加高效、便捷的数据处理工具。 火语言也属于同创始团队的旗下产品&#xff0c;历经三年每周版本更新&#xff0c;迭…

电感七大关键参数

大家好,这里是大话硬件。 今天这篇文章介绍电感的七大关键参数。 1、电感值 电感值就是电感做好以后的固有特性,比如1uH, 10mH,1H,这样不同类型的感值。在学习电感值之前,我们先看一下电阻公式: 其中p是导体的电阻率(Ω*m),S是导体的横截面积(m2),l是导体的长度…

Linux驱动学习——Linux启动流程

什么是驱动 驱动&#xff0c;即设备驱动程序&#xff0c;是一种可以使计算机和设备通信的特殊程序。 从作用角度来看&#xff0c;驱动的主要功能是将硬件设备的功能与操作系统进行连接&#xff0c;让操作系统能够识别并正确使用硬件设备。例如&#xff0c;显卡驱动能让操作系…

Java SE-object类和里面的3个主要方法解读

文章目录 1.object类2.toString方法调用过程2.1具体案例2.2源代码查看2.3方法的重写2.4重写效果 3.equals方法调用过程3.1现象的描述3.2方法的重写3.3IDEA自动填充 4.hashcode方法 1.object类 java里面除了object类&#xff0c;所有的类都是存在继承关系的&#xff0c;object类…

【Docker】03-自制镜像

1. 自制镜像 2. Dockerfile # 基础镜像 FROM openjdk:11.0-jre-buster # 设定时区 ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 拷贝jar包 COPY docker-demo.jar /app.jar # 入口 ENTRYPOINT ["ja…

javaweb-请求和响应

1.http协议 1.1请求 1.2.响应 常见响应状态码&#xff1a; 状态码大全&#xff1a;https://cloud.tencent.com/developer/chapter/13553 常见的响应头&#xff1a; 2.请求和响应 2.1.请求 2.1.1postman 作用&#xff1a;常用于接口测试 使用&#xff1a;官网安装-->注册…

电脑手机下载小米xiaomi redmi刷机包太慢 解决办法

文章目录 修改前下载速度修改后下载速度修改方法&#xff08;修改host&#xff09; 修改前下载速度 一开始笔者以为是迅雷没开会员的问题&#xff0c;在淘宝上买了一个临时会员后下载速度依然最高才100KB/s 修改后下载速度 修改方法&#xff08;修改host&#xff09; host文…

【星汇极客】STM32 HAL库各种模块开发之DHT11模块

前言 本人是一名嵌入式学习者&#xff0c;在大学期间也参加了不少的竞赛并获奖&#xff0c;包括&#xff1a;江苏省电子设计竞赛省一、睿抗机器人国二、中国高校智能机器人国二、嵌入式设计竞赛国三、光电设计竞赛国三、节能减排竞赛国三等。 暑假的时候参加了太多的比赛&#…

nacos启动报错:Unable to start embedded Tomcat

报错截图&#xff1a; 解决方法&#xff1a;编辑启动脚本/nacos/bin/startup.sh&#xff0c;指定模式为standalone即可 修改后启动成功~

vscode配置golang

1.安装golang解释器 从网址https://go.dev/dl/下载对应的golang解释器 2.配置环境 Extensions中搜索安装go 2.配置settings.json {"go.autocompleteUnimportedPackages": true,"go.gocodeAutoBuild": false,"explorer.confirmPasteNative"…

若依权限设计与自定义新增用户

前言 若依 系统的权限设计是基于RBAC&#xff08;Role-Based Access Control&#xff09;&#xff0c;即基于角色的访问控制模型&#xff0c;允许通过角色来管理用户的权限。 每个用户可以分配一个或多个角色。用户的权限来自于其所分配的角色。用户与角色的对应关系保存在 sys…

llama3中文版微调

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

数据结构--List的介绍

目录 1. 什么是List Collection中有那些方法&#xff1f; add(E e)方法 addAll(Collection c)方法 clear()方法 contains(Object o)方法 containsAll(Collection c)方法 equals(Object o)方法 hashCode()方法 isEmpty()方法 iterator()方法 remove(Object o)方法 …

[OS] 编译 Linux 内核

编译 Linux 内核&#xff1a;详细教程与 Kthreads 入门结合 我们将学习如何编译 Linux 内核&#xff0c;同时结合 Kthreads 的知识来理解各个步骤的目的。对于虚拟环境下的开发环境配置&#xff0c;本文将为你提供逐步指导。 1. 下载内核源代码 首先&#xff0c;我们需要从官…

数据结构——栈与队列的实现(全码)

一 栈的概念 栈是一种特殊的线性表&#xff0c;栈内数据遵循先进后出(LIFO)的原则&#xff0c;对于栈&#xff0c;只能在同一侧进行入栈和出栈操作。 入栈操作和出栈操作是在栈的同一侧进行的&#xff0c;如图示&#xff1a; 对于栈这种数据类型&#xff0c;我们可以采用链表或…