数据结构 - 栈 与 队列 - (java)

news2024/9/29 18:49:23

前言

本篇介绍栈和队列,了解栈有顺序栈和链式栈,队列底层是双链表实现的,单链表也可以实现队列,栈和队列的相互实现和循环队列;如有错误,请在评论区指正,让我们一起交流,共同进步!


文章目录

  • 前言
  • 1. 栈的认识
    • 1.1 栈的使用:栈实现队列
  • 2. 队列的认识
    • 2.1 双队列实现栈
    • 2.2 循环队列 - 数组实现的队列
  • 3. 双端队列
  • 总结

本文开始

1. 栈的认识

栈:一种特殊的线性表,只能从一头进入,一头出;
进出栈规则:先进后出

栈的模拟实现:顺序栈和链式栈实现栈时间复杂度都是O(1)
顺序栈:栈可以使用顺序表实现
链式栈:可以用单链表实现:头插和头删(入栈) 或 尾插和尾删(出栈);
可以使用双链表实现;既可以头进头出,也可以尾进尾出;(双链表知道前后节点位置)

在这里插入图片描述

链式栈代码实现:

public static void main(String[] args) {
        //链表实现栈:LinkedList底层是双链表
        LinkedList<Integer> stack = new LinkedList<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        System.out.println(stack.pop());
    }

代码实现栈的基本操作:

public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        //压栈:栈中添加元素
        stack.push(2);
        stack.push(3);
        //看栈顶元素
        System.out.println(stack.peek());
        //栈的大小
        System.out.println(stack.size());
        //出栈
        System.out.println(stack.pop());
        //栈是否为空
        System.out.println(stack.isEmpty());
    }

1.1 栈的使用:栈实现队列

双栈实现队列代码:
思路:
一个栈1表示入队,一个栈2表示出队;
队列出队需要判断栈2是否为空,栈2空将栈1中元素全部放到栈2中,此时再出栈2栈顶元素即可;
入队:看栈2是否有元素,栈2有元素直接返回栈顶元素;栈2为空,再将栈1中元素放到栈2中,才能看到出队的值;

public class MyQueue {
    Stack<Integer> stack1;
    Stack<Integer> stack2;
    //创建两个栈
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    //在栈1中放元素
    public void push(int x) {
        stack1.push(x);
    }
    public int pop() {
        if(empty()) {
            return -1;
        }
        //判断栈2中是否有元素
        if(stack2.isEmpty()) {
            //栈1元素全部放到栈2中
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        //不空,栈2中有元素直接弹出
        return stack2.pop();
    }

    public int peek() {
        if(empty()) {
            return -1;
        }
        //看栈2是否有元素
        if(stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        //栈2有元素
        return stack2.peek();
    }
    //判断两个栈是否为空
    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }
}

2. 队列的认识

1.队列:也是一种特殊的线性表,一头进,另一头出;
进出队列规则:先进先出;

2.链表实现队列:
单链表实现队列两种方式: - ==使用一种下标
头插法:进队-时间复杂的O(1) - 头插 ,出队-时间复杂的O(n) - 尾删
尾插法:进队-时间复杂的O(n) - 尾插,出队-时间复杂的O(1) - 头删

(两个下标控制头尾)单链表实现队列代码实现:
思想:使用头删尾插,进队出队时间复杂都是O(1);使用两个下标,记录头节点位置head,再记录尾节点位置last; 方便头插(入队),尾删(出队);
【注】单链表实现队列只能尾插头删,保证时间复杂度都为O(1)
不使用头插尾删原有?
使用头插尾删进行单链表,进队-头插时间复杂度O(1), 出队-尾删时间复杂度O(n);
尾删:每次删除都需要找尾节点前一个节点位置,需要遍历一般链表所以时间复杂度高;
代码:

public class MyQueue {
    static class Node {
        int val;
        Node next;
        public Node(int val) {
            this.val = val;
        }
    }
    //用双下标实现队列,需要定义两个
    public Node head;//头下标
    public Node last;//尾下标
    public int size;
    //入队操作: 插入
    public boolean offer(int val) {
        //插入需要新节点,创建新节点
        Node node = new Node(val);
        //没有节点的时候
        if(head == null) {
            //头尾下标指向同一位置
            head = node;
            last = node;
        }else {
            //head != null
            //链接新节点
            last.next = node;
            //尾节点向后移动一步
            last = node;
        }
        size++;//计数
        return true;
    }
    //出队:删除头删
    public int poll(int val) {
        //判断链表是否为空
        if(isEmpty()) {
            return -1;
        }
        //记录删除的值
        int v = head.val;
        head = head.next;
        //如果只有一个节点,lasta也需要置空
        if(head == null) {
            last = null;
        }
        size--;//-1
        return v;
    }
    private boolean isEmpty() {
        return size == 0;
    }
    public int peek() {
        //链表为空不用看队头元素
        if(isEmpty()) {
            return -1;
        }
        return head.val;//返回队头元素
    }
    public int getSize() {
        //队列大小
        return size;
    }
}

双链表实现队列代码实现:
特点:双链表实现队列可以自由头进尾删,头删尾进;

public class MyQueue2 {
    //双链表实现队列
    static class Node {
        int val;
        Node prev;
        Node next;
        public Node(int val) {
            this.val = val;
        }
    }
    //前后下标
    public Node front;
    public Node last;
    public int size;
    //入队
    public boolean offer(int val) {
        //插入的新节点
        Node newNode = new Node(val);
        //没有节点
        if(front == null) {
            front = newNode;
            last = newNode;
        }else {
            //不为空
            //链接前后节点
            newNode.prev = last;
            last.next = newNode;
            //后下标后移
            last = newNode;
        }
        size++;
        return true;
    }
    //出队:删除
    public int poll() {
        int v = -1;
        //队列为空
        if(isEmpty()) {
            return -1;
        }
        //只有一个节点
        if(front == last) {
            front = null;
            last = null;
        }else {
            //先记录值
            v = front.val;
            //前下标后移
            front = front.next;
            //找到前一个下标的next置为空
            front.prev.next = null;
            //当前prev置为空:防止空指针异常
            front.prev = null;
        }
        size--;
        return v;
    }

    private boolean isEmpty() {
        return size == 0;
    }
    public int peek() {
        if(isEmpty()) {
            return -1;
        }
        return front.val;
    }
}

队列基本操作代码实现:

   public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        //入队:队列中添加元素
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        //看队头元素
        System.out.println(queue.peek());
        //队列的大小
        System.out.println(queue.size());
        //出队
        System.out.println(queue.poll());
        //队列是否为空
        System.out.println(queue.isEmpty());
    }

2.1 双队列实现栈

双队列实现栈:
思路:
①先定义两个队列
②入栈:是判断那个队列不为空,队列1不为空,就往队列1中放,队列2不为空,就往队列2中放,都为空默认往队列1中放;
③出栈:假设不为空队列元素个数为size个,将不为空的队列出队size-1个到另一个为空的队列,出队列size-1个队列剩余一个就为出栈元素;
④栈顶元素:假设队列1不为空,队列2空;定义一个变量为tmp, 作为队列1元素到队列2元素的过度,将队列1中元素全部传到队列2中,此时队列1最后出队的元素就是栈顶元素,并存储在tmp中,返回tmp即可;

在这里插入图片描述

代码实现:

import java.util.LinkedList;
import java.util.Queue;

public class MyStack {
    //双队列实现栈
    //构建双队列
    Queue<Integer> queue1;
    Queue<Integer> queue2;
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }

    public void push(int x) {
        //两个对谁不为空,就入那个队
        if(!queue1.isEmpty()) {
            queue1.offer(x);
        }else if(!queue2.isEmpty()) {
            queue2.offer(x);
        }else {
            //都为空,入第一个队
            queue1.offer(x);
        }
    }

    public int pop() {
        //判断队列是否为空
        //都为空
        if(empty()) {
            return -1;
        }
        if(!queue1.isEmpty()) {
            //获取队列1大小
            int size = queue1.size();
            //队1出size-1个元素,到队2中
            while (size - 1 != 0) {
                queue2.offer(queue1.poll());
                size--;
            }
            //队1只剩1个,就是要出栈的元素
            return queue1.poll();
        }else {
            //队1为空,队2不为空
            //获取队列2大小
            int size = queue2.size();
            //队2出size-1个元素,到队1中
            while (size - 1 != 0) {
                queue1.offer(queue2.poll());
                size--;
            }
            //队2只剩1个,就是要出栈的元素
            return queue2.poll();
        }
    }

    public int top() {
        //判断队列是否为空
        //都为空
        if(empty()) {
            return -1;
        }
        if(!queue1.isEmpty()) {
            //获取队列1大小
            int size = queue1.size();
            int tmp = -1;//存储每个出队元素
            while (size != 0) {
                tmp = queue1.poll();
                queue2.offer(tmp);
                size--;
            }
            //队1最后一个出队的,就是要栈顶的元素
            return tmp;
        }else {
            //获取队列2大小
            int size = queue2.size();
            int tmp = -1;//存储每个出队元素
            while (size != 0) {
                tmp = queue2.poll();
                queue1.offer(tmp);
                size--;
            }
            //队2最后一个出队的,就是要栈顶的元素
            return tmp;
        }
    }

    public boolean empty() {
        return queue1.isEmpty() && queue2.isEmpty();
    }
}

2.2 循环队列 - 数组实现的队列

循环队列:可以看成一圈型的队列,但其实还是数组

为什么使用循环?
一个存满的数组,先出队一个,如果再进队尾rear下标就越界了,但数组中还有空间没有利用 =》 对于这种情况所以使用了循环;

在这里插入图片描述

怎么实现循环?
1.可以使用下标标记法,进队一个标记一个,从而实现循环;
2.牺牲一个空间,使用求余来实现循环 ( rear + 1) % length;(循环需要首尾相连,如图从7下标到0下标,求余就可以实现;)

实现循环队列:
思路分析:
判断循环队列是否为空还是满,就使用牺牲一个空间法,(rear + 1) == front 判断为满;
rear == front 判断为空;如下图
怎样实现循环:使用求余数的方法,可以让下标从尾下标到开始下标(如图0下标到7下标);

在这里插入图片描述

循环队列代码:

class MyCircularQueue {
    //循环链表:底层是数组,所以创建数组
    int[] elem;
    //循环的前后下标
    int front;//前
    int rear;//后
    public MyCircularQueue(int k) {
        //初始化k大小的数组
        elem = new int[k + 1];
    }
    //进队
    public boolean enQueue(int value) {
        //进队先判断队列是否满
        if(isFull()) {
            return false;
        }
        //不满进队
        elem[rear] = value;
        //rear++ =》不能使为下标到起始下标,进行循环,所以使用求余数;
        rear = (rear + 1) % elem.length;
        return true;
    }
    //出队
    public boolean deQueue() {
        //出队先判断队列是否有元素
        if(isEmpty()) {
            return false;
        }
        //前下标+1,与需要考虑构成循环,末尾到开始位置
        front = (front + 1) % elem.length;
        return true;
    }
    //获取队头元素
    public int Front() {
        if(isEmpty()) {
            return -1;
        }
        return elem[front];//不为空就返回
    }
    //获取队尾元素
    public int Rear() {
        if(isEmpty()) {
            return -1;
        }
        //牺牲一个空间法,尾下标超过尾元素1个数组空间 =》所以一般情况:尾下标需要-1才是尾元素
        //会遇到一种尾下标在(0位置)起始位置,而尾元素在最后位置,需要构成循环 =》 这里特殊情况,特殊出来0下标位置
        int index = (rear == 0) ? elem.length - 1 : rear - 1;
        return elem[index];
    }
    //判断是否为空
    public boolean isEmpty() {
        return rear == front;
    }
    //判断是否满
    public boolean isFull() {
        //循环队列使用牺牲1个空间方法,区分空和满
        //rear+1 再余 =>构成循环,尾下标就能够到起始下标;
        return (rear + 1) % elem.length == front;
    }
}

3. 双端队列

双端队列:是一种继承Queue的接口,可以用它实现栈与队列;
实现栈,队列:

  		Deque<Integer> stack1 = new ArrayDeque<>();
        Deque<Integer> stack2 = new LinkedList<>();
        Deque<Integer> queue1 = new LinkedList<>();

总结

✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!

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

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

相关文章

Splashtop:支持M1/M2芯片 Mac 电脑的远程控制软件

M1和M1芯片的Mac电脑现在越来越多了。M1和M2的强大性能&#xff0c;让使用者们办公、娱乐如虎添翼。 M1 芯片于2020年11月11日推出&#xff0c;是Apple 首款专为Mac打造的芯片&#xff0c;拥有格外出色的性能、众多的功能&#xff0c;以及令人惊叹的能效表现。M1 也是Apple 首款…

Docker搭建MySQL主主模式+Keepalived实现高可用集群

1.环境准备&#xff1a; 系统版本 Centos7 IP&#xff1a;10.10.11.79 master IP&#xff1a;10.10.11.80 backup 虚拟ip&#xff1a;10.10.11.82 Docker环境&#xff1a; centos7离线安装docker&#xff0c;docker-compose keepalived安装包&#xff1a; https://www.k…

设计模式之原型模式与建造者模式详解和应用

目录1 原型模式1.1 原型模式定义1.2 原型模式的应用场景1.3 原型模式的通用写法&#xff08;浅拷贝&#xff09;1.4 使用序列化实现深度克隆1.5 克隆破坏单例模式1.6 原型模式在源码中的应用1.7 原型模式的优缺点1.8 总结2 建造者模式2.1 建造者模式定义2.2 建造者模式的应用场…

自动化测试基础概念

前端自动化测试相关概念&#xff08;单元测试、集成测试、E2E 测试、快照测试、测试覆盖率、TDD 以及 BDD 等内容&#xff09;&#xff0c;以及测试框架 Jest 的讲解&#xff0c;Vue.js 应用测试&#xff0c;React 应用测试&#xff0c;E2E 测试&#xff0c;Node.js 应用测试等…

操作系统(三):内存管理,分页地址映射,页面置换算法LRU,NRU,FIFO,第二次机会算法和时钟算法

文章目录一、虚拟内存二、分页系统地址映射三、页面置换算法最佳置换算法LRU 最近最久未使用算法NRU 最近未使用FIFO 先进先出第二次机会算法时钟算法四、内存分段五、段页式六、分页和分段的比较一、虚拟内存 虚拟内存的目的是为了让无力内存扩充成更大的逻辑内存&#xff0c…

手工测试怎样找到第一份自动化测试的工作?

你会手工测试了&#xff0c;那么准备再找自动化测试的工作&#xff0c;相对于什么都不懂的小白来说优势很大。要找自动化测试的工作&#xff0c;首先你要会自动化测试&#xff0c;你要会编程&#xff0c;能写自动化测试脚本的语言有很多&#xff0c;你可以自己选择一门&#xf…

求职笔记记录

求职笔记记录目录概述需求&#xff1a;设计思路实现思路分析1.面试过程参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,challenge Survive.…

tofu:一款功能强大的模块化Windows文件系统安全测试工具

关于tofu tofu是一款功能强大的针对Windows文件系统安全的模块化工具&#xff0c;该工具可以使用离线方法对目标Windows文件系统进行渗透测试&#xff0c;并通过绕过Windows系统登录界面来帮助广大研究人员测试Windows主机的安全性。除此之外&#xff0c;该工具还可以执行哈希…

Python学习笔记 --- 初识Python

编译器和解释器 概念&#xff1a; 可以把编译器和解释器理解成是人与计算机之间的 “翻译”&#xff0c;将代码翻译成计算机能够识别的指令。 编译器&#xff1a;全文翻译&#xff0c;拿到代码会将其编译成一个计算机能够识别的临时文件&#xff0c;再把文件交给操作系统读取…

SpringMVC(3)

通过上面的学习我们知道&#xff0c;默认情况下无论是Spring MVC还是SpringBoot返回的都是现在都是前后端分离的&#xff0c;后端只需要进行返回数据给前端数据即可 1)向浏览器返回一个HTML界面 请求参数的数据类型Contnt-Type:text/html&#xff1b;charsetutf-8 RequestMapp…

Linux centos、麒麟V10 安装redis

目录 1、下载redis安装包 2、将下载后的.tar.gz压缩包上传到到服务器自定义文件夹下 3、 解压文件 4、安装redis 5、配置redis.conf 6、启动redis 1、下载redis安装包 Redis各版本下载&#xff1a;https://download.redis.io/releases/ 2、将下载后的.tar.gz压缩包上传到…

用主动游泳的三维水母模型量化美杜莎的(medusan)机械空间的性能(二)(2017)

文章目录用主动游泳的三维水母模型量化美杜莎的&#xff08;medusan&#xff09;机械空间的性能&#xff08;二&#xff09;(2017)原文链接&#xff1a;https://doi.org/10.1017/jfm.2017.3结果3.1 参考案例的游泳动力学3.2 改变钟的主动和被动材料属性3.2.1 改变施加的张力3.2…

【并发编程】【1】概览

并发编程 1. 概览 1.1 这门课讲什么 这门课中的【并发】一词涵盖了在Java平台上的 进程线程并发并行 以及Java并发工具&#xff0c;并发问题以及解决方案&#xff0c;同时也会包含一些其他领域的开发 1.2 为什么学这么课程 我工作中用不到并发啊&#xff1f; 1.3 课程特…

qemu的snapshot快照功能的详细使用介绍

快照功能还是蛮有趣的&#xff0c;就是资料比较少&#xff0c;这边万能菜道人特意整理了一下。参考内容&#xff1a;QEMU checkpoint(snapshot) 使用-pudn.comKVM&QEMU学习笔记&#xff08;二&#xff09;-蒲公英云 (dandelioncloud.cn)在线迁移存储 - 爱码网 (likecs.com)…

SpringMVC DispatcherServlet源码(4) HandlerMapping和HandlerAdapter等组件说明

本文介绍一下与DispacherServlet相关的几个重要组件&#xff1a; HandlerMapping - 管理请求与处理器映射关系HandlerAdapter - 请求处理器HandlerMethodArgumentResolver - 处理器方法参数解析器HandlerMethodReturnValueHandler - 处理器方法返回值处理器HttpMessageConvert…

LeetCode(剑指offer) DAY2

1.题目 从尾到头打印链表 解法一&#xff1a;先统计链表有多少节点&#xff0c;然后创建数组&#xff0c;再次遍历链表将值存储至数组&#xff0c;然后反转数组。这种解法思路简单&#xff0c;但是时间复杂度较高。 class Solution {int a 0,b0;public int[] reversePrint(Li…

Routability-Driven Macro Placement with Embedded CNN-Based Prediction Model

Routability-Driven Macro Placement with Embedded CNN-Based Prediction Model 2019 Design, Automation & Test in Europe Conference & Exhibition (DATE) DOI: 10.23919/DATE.2019.8715126 目录Abstract一、Introduction二、PROBLEM FORMULATION AND PRELIMINARIE…

java分析插入排序

首先查看一张经典的插入排序的图片 有图片可知&#xff0c;插入排序其字面的意思找到小的插入到前面就行 插入排序的基本思想就是分治&#xff1a;将数组分为两个区域&#xff0c;分别是已排序区和没有排序的区域 已排序区&#xff1a;假定一边的数组是都排序好的 wei排序区&…

软件测试选Python还是Java?

目录 前言 1、先从一门语言开始 2、两个语言的区别 3、两个语言的测试栈技术 4、如何选择两种语言&#xff1f; 总结 前言 对于工作多年的从业者来说&#xff0c;同时掌握java和Python两门语言再好不过&#xff0c;可以大大增加找工作时的选择范围。但是对于转行的人或者…

Vue2之Vue-cli应用及组件基础认识

Vue2之Vue-cli应用及组件基础认识一、Vue-cli1、单页面应用程序2、vue-cli介绍3、安装和使用4、创建项目4.1 输入创建项目4.2 选择第三项&#xff0c;进行自主配置&#xff0c;按回车键即可4.3 选择自己需要的库4.4 选择Vue的版本4.5 选择CSS选择器4.6 选择Babel、ESLint、etc等…