【Java 数据结构】队列的实现及相关OJ题

news2024/11/17 15:30:28

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!

欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心


目录

一、队列的基本概念

1.什么是队列

2.普通队列和双端队列

​3.关于队列一些操作的实现

4. 单向链表实现队列

5. 双向链表实现队列

6.循环队列

二、队列相关OJ题

1.设计循环队列

2.  用栈实现队列​编辑​

3. 用队列实现栈



一、队列的基本概念

1.什么是队列

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

2.普通队列和双端队列


3.关于队列一些操作的实现

①普通队列 

Queue(一般多使用返回特殊值的处理,更加方便)

错误处理抛出异常返回特殊值
入队列add(e)offer(e)
出队列remove()poll()
队首元素element()peak()

方法简单演示: 

public class Test {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);
        queue.offer(2);
        System.out.println("该队列队头元素为:");
        System.out.println(queue.peek());
        System.out.println("出队元素为");
        System.out.println(queue.poll());
    }
}

②双端队列

Deque(一般多使用返回特殊值的处理,更加方便)

头部/尾部头部元素(队首)尾部元素(队尾)
错误处理抛出异常返回特殊值抛出异常返回特殊值
入队列addFirst(e)offerFirst(e)addLast(e)offerLast(e)
出队列removeFirst()pollFirst()removeLast()pollLast()
获取元素getFirst()peekFirst()getLast()peekLast()

方法简单演示: 

import java.util.*;
 
public class Test {
    public static void main(String[] args) {
        Deque<Integer> queue = new LinkedList<>();
      queue.offerFirst(2);
      queue.offerLast(1);
        System.out.println("该队列队头元素为:");
        System.out.println(queue.peek());
        System.out.println("出队元素为");
       System.out.println(queue.poll());
    }
}

  ● 顺序队:Queue接口+实现类ArrayDeque

Queue<Integer> queue = new ArrayDeque<>();//顺序队列

   ● 链式队:Queue接口+实现类LinkedList

Queue<Integer> queue2 = new LinkedList<>();//链式队列

4. 单向链表实现队列

         ● 实现代码:

class Node{
    public int val;
    public Node next;
    public int usedSize;
    public Node(int val){
        this.val=val;
    }
}

public class MyQueue {
    public Node head;
    public Node last;

    //入队,实质上是一个尾插法
    public void offer(int val){
        Node node=new Node(val);
        if(head==null){
            head=node;
            last=node;
        }else {
            last.next=node;
            last=last.next;
        }
        usedSize++;
    }

    //出队
    public int poll(){
        if(isEmpty()){
            throw new RuntimeException("队列为空!");
        }
        int oldVal=head.val;
        this.head=head.next;
        if(head == null) {
            last null;
        }
        usedSize--;
        return oldVal;
    }
    
    //是否为空
    public boolean isEmpty(){
        return this.usedSize == 0;
    }

    //查看栈顶元素
    public int peek( ){
        if(isEmpty()) {
            throw new RuntimeException("队列为空!");
        }
        return head.val;
    }
    public int getUsedSize() {
        return usedSize;
    }
}

5. 双向链表实现队列

public class Queue {
    // 双向链表节点
    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 -1;
        }
        return first.value;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return first == null;
    }
}

6.循环队列

循环队列的引入:


二、队列相关OJ题

1.设计循环队列

         ● 写题链接:225. 用队列实现栈 - 力扣(LeetCode)

         ● 解题思路:front代表队头元素的下标,rear代码队尾元素的下标,每次入队出队都用取模公式来维护,入队元素增加,队尾rear下标的位置更新到(rear+1) % elem.length位置上,出队元素减少,队头front下标的位置也更新到(rear+1) % elem.length位置上,意获取队尾元素的时候,如果rear==0说明队尾元素已经走到该数组的最后一个位置了,因为我们底层是用数组维护的,防止越界.

注意:

在循环队列中,我们添加元素时,元素被放置在队尾指针 rear 当前的位置,然后 rear 会向后移动一步。所以,真正的元素存储在 rear - 1 的位置上(当 rear 为 0 时,元素存储在数组的末尾位置)。这意味着,在任何时刻,rear 所指向的位置都是空的。

rear 的下一个位置就是队头指针 front 时,说明队列已经满了。这时,我们不能再向队列中添加元素,因为这将使得队头指针和队尾指针指向同一位置,导致队列为空和队列已满的歧义。所以,在这种情况下,我们不会在当前 rear 所指向的位置放置元素,保持这个位置为空,以区分队列为空和队列已满的状态。

           ● 实现代码:

class MyCircularQueue {

    public int[] elem; // 存储队列元素的数组
    public int front; // 队头下标
    public int rear; // 队尾下标

    public MyCircularQueue(int k) {
        // 因为牺牲了一个空间,所以数组长度+1
        this.elem = new int[k + 1];
    }

    // 入队操作
    public boolean enQueue(int value) {
        if (isFull()) return false; // 如果队列已满, 直接返回 false
        this.elem[rear] = value; // 将值放入队尾位置
        rear = (this.rear + 1) % elem.length; // 更新队尾下标,使用取模运算实现循环
        return true;
    }

    // 出队操作
    public boolean deQueue() {
        if (isEmpty()) return false; // 如果队列为空, 直接返回 false
        // 不用管原数,会被新入队的数直接替代
        front = (this.front + 1) % elem.length; // 更新队头下标,使用取模运算实现循环
        return true;
    }

    // 得到队头元素
    public int Front() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空"); // OJ上改为 return -1;
        }
        return this.elem[front]; // 返回队头元素
    }

    // 得到队尾元素
    public int Rear() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空"); // OJ上改为 return -1;
        }
        int index = 0;
        if (rear == 0) {
            index = elem.length - 1; // 当队尾下标为0时,队尾元素位于数组末尾
        } else {
            index = rear - 1; // 其他情况下,队尾元素位于队尾下标前一个位置
        }
        return this.elem[index]; // 返回队尾元素
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return front == rear; // 队头下标等于队尾下标时, 队列为空
    }

    // 判断队列是否已满
    public boolean isFull() {
        // 判断队尾下一个位置是队头就是满了
        if ((this.rear + 1) % elem.length == front) {
            return true;
        }
        return false;
    }
}

2.  用栈实现队列

       ● 写题链接:232. 用栈实现队列 - 力扣(LeetCode)

       ● 写题思路:

           1) 因为队列是先进先出,所以我们要借用两个栈实现

           2) 一个栈A存入队的序列,另一个栈B为出队的序列,栈B的数据由栈A出栈得到

           3) 出数据从栈B出,如果栈B为空则从栈A中获取,再由栈B出数据,实现数据的先进先出。

         ● 图示 

        ● 实现代码:

class MyQueue {
    Deque<Integer> s1; // 入队栈
    Deque<Integer> s2; // 出队栈

    public MyQueue() {
        s1 = new ArrayDeque<Integer>(); // 初始化入队栈
        s2 = new ArrayDeque<Integer>(); // 初始化出队栈
    }

    public void push(int x) {
        s1.push(x); // 将元素推入入队栈
    }

    public int pop() {
        if (empty()) { // 如果队列为空
            return -1;
        }
        if (s2.isEmpty() && !s1.isEmpty()) { // 如果出队栈为空且入队栈非空
            while (!s1.isEmpty()) { // 将入队栈的所有元素倒入出队栈
                s2.push(s1.pop());
            }
            return s2.pop(); // 弹出出队栈顶部元素
        }
        return s2.pop(); // 弹出出队栈顶部元素
    }

    public int peek() {
        if (empty()) { // 如果队列为空
            return -1;
        }
        if (s2.isEmpty() && !s1.isEmpty()) { // 如果出队栈为空且入队栈非空
            while (!s1.isEmpty()) { // 将入队栈的所有元素倒入出队栈
                s2.push(s1.pop());
            }
            return s2.peek(); // 返回出队栈顶部元素,但不弹出
        }
        return s2.peek(); // 返回出队栈顶部元素,但不弹出
    }

    public boolean empty() {
        return s1.isEmpty() && s2.isEmpty(); // 当入队栈和出队栈都为空时,队列为空
    }
}

3. 用队列实现栈

        ● 写题链接:225. 用队列实现栈 - 力扣(LeetCode)

        ● 写题思路:

          1) 因为栈是先进后出,所以我们要借用两个队列实现

          2) 哪个队列不为空则往那个队列入数据,如果都为空,默认往队列qu1入数据

          3) 出数据时要先将数据不为空的队列的前length - 1个元素入到另一个空队中,然后将最后一个元素出队。

注:查看栈顶元素不需要将最后一个元素出队,要保存它,且继续把它入到另一个空队中。

        ● 图示 

       ● 实现代码:

class MyStack {
    Queue<Integer> q1; // 第一个队列
    Queue<Integer> q2; // 第二个队列

    public MyStack() {
        q1 = new LinkedList(); // 初始化第一个队列
        q2 = new LinkedList(); // 初始化第二个队列
    }

    public void push(int x) {
        if (!q1.isEmpty()) { // 如果第一个队列非空
            q1.offer(x); // 将元素添加到第一个队列末尾
        } else if (!q2.isEmpty()) { // 如果第二个队列非空
            q2.offer(x); // 将元素添加到第二个队列末尾
        } else {
            if (q1.isEmpty() && q2.isEmpty()) { // 如果两个队列都为空
                q1.offer(x); // 将元素添加到第一个队列末尾
            }
        }
    }

    public int pop() {
        if (empty()) { // 如果栈为空
            return -1;
        }
        if (!q1.isEmpty()) { // 如果第一个队列非空
            int size = q1.size();
            for (int i = 0; i < size - 1; i++) { // 将前 size-1 个元素移动到第二个队列
                int val = q1.poll();
                q2.offer(val);
            }
            return q1.poll(); // 返回并移除第一个队列的最后一个元素
        } else {
            int size = q2.size();
            for (int i = 0; i < size - 1; i++) { // 将前 size-1 个元素移动到第一个队列
                int val = q2.poll();
                q1.offer(val);
            }
            return q2.poll(); // 返回并移除第二个队列的最后一个元素
        }
    }

    public int top() {
        if (empty()) { // 如果栈为空
            return -1;
        }
        if (!q1.isEmpty()) { // 如果第一个队列非空
            int size = q1.size();
            int val = -1;
            for (int i = 0; i < size; i++) { // 将所有元素移动到第二个队列,同时记录最后一个元素的值
                val = q1.poll();
                q2.offer(val);

            }
            return val; // 返回最后一个元素的值,即栈顶元素
        } else {
            int size = q2.size();
            int val = -1;
            for (int i = 0; i < size; i++) { // 将所有元素移动到第一个队列,同时记录最后一个元素的值
                val = q2.poll();
                q1.offer(val);
            }
            return val; // 返回最后一个元素的值,即栈顶元素
        }
    }

    public boolean empty() {
        if (q1.isEmpty() && q2.isEmpty()) { // 如果两个队列都为空
            return true; // 栈为空
        } else {
            return false; // 栈非空
        }
    }
}

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

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

相关文章

图的搜索算法---8.2 ZOJ1002-Fire Net--合理布置碉堡

目录 问题&#xff1a; 分析&#xff1a; 主要的算法代码&#xff1a; 完整代码&#xff1a; 出问题的代码&#xff1a; 原因&#xff1a; 正确代码&#xff1a; 代码分析&#xff1a; 算法函数讲解&#xff1a; CanPut函数 solve函数 运行结果&#xff1a; 问题&am…

勇创世界一流!移动云为我国数字经济发展提供有力支撑

今年2月&#xff0c;中共中央、国务院印发了《数字中国建设整体布局规划》&#xff0c;通过顶层设计及布局&#xff0c;擘画了我国数字经济发展蓝图。近日在国新办举行的第六届数字中国建设峰会新闻发布会上&#xff0c;相关负责人也对我国数字经济现阶段取得的成绩进行了总结&…

IPWorks VoIP 2022 Crack

网络电话组件 IPWorks VoIP 提供 SIP 和 IVR 组件&#xff0c;旨在促进 CTI 应用程序中的常见 VoIP 操作。快速集成功能以建立拨出电话、接听来电以及根据您的自定义 IVR 菜单路由呼叫。还支持其他 SIP 功能&#xff0c;例如文本到语音、播放预先录制的消息、通话录音和电话会议…

总结:helm

一、介绍 Helm是k8s的包管理工具&#xff0c;类似Linux系统常用的 apt、yum等包管理工具&#xff0c;基于go 语言开发。使用helm可以简化k8s应用部署 二、基本概念 Helm的基本概念 Chart&#xff1a;一个 Helm 包&#xff0c;其中包含了运行一个应用所需要的镜像、依赖和资源…

flask实际开发:flask和nginx如何配置支持websocket

使用pycharm启动flask项目有坑&#xff0c;先修改pycharm设置 1、点击Edit Confiturations 2、配置启动方式 1 新增启动配置 2 选择使用python命令执行 3 给配置设置一个名字 4 设置要启动的模块的位置&#xff0c;flask基本都是app.py 模块 最后别忘记&#xff1a;点击右侧的…

Linux网络-传输层UDP/TCP详解

目录 计算机网络的层状结构 UDP协议 UDP报文格式 理解UDP/TCP报文的本质 UDP的特点 UDP的缓冲区 sendto/recvfrom/send/recv/write/read IO类接口 UDP是全双工的 UDP注意事项 UDP协议&#xff0c;实现简单聊天室&#xff08;服务端客户端&#xff09; TCP协议 TCP协…

SpringBoot集成 ElasticSearch

Spring Boot 集成 ElasticSearch 对于ElasticSearch比较陌生的小伙伴可以先看看ElasticSearch的概述ElasticSearch安装、启动、操作及概念简介 好的开始啦~ 1、基础操作 1.1、导入依赖 <dependency><groupId>org.springframework.boot</groupId><arti…

【是C++,不是C艹】 第一个C++程序 | 命名空间 | 输入输出

&#x1f49e;&#x1f49e; 欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e; &#x1f449;专栏&#xff1a;《是C&#xff0c;不是C艹》&#x1f448; 前言&#xff1a; 在认识了C的来历之后&#xff0c;我们就要开始正式学习C了&#xff0c;系好安全带&#xff0c;准备…

阿里云安全ACP认证考试实验之云盾之云安全中心与态势感知入门体验

“更多玩转云产品” 1、实验概述 通过本实验可对云安全中心&#xff0c;态势感知的一些基本操作有深入了解以及如何来对实例进行安全监控 2、实验目标 完成此实验可以掌握的能力有&#xff1a; 在安骑士中添加白名单、登录安全设置 通过态势感知查看实例的相关告警威胁 3…

数值区间的模糊匹配,二分查找的应用

先看图: 需求很明确,要根据左边的值,显示右边的值。 比如,现在拿到的值是 17.12,那么应该显示成 15;拿到 17.599 ,那么应该显示成 20. 先找规律: 为了便于说明,暂且将左边的值设为 x, 右边的值设为 y. 第一行和最后一行可以写死成 0 与 1500;余下的每行,x 的区间是…

Vue CLI 创建一个项目

vue create 运行以下命令来创建一个新项目&#xff1a; vue create hello-world警告 如果你在 Windows 上通过 minTTY 使用 Git Bash&#xff0c;交互提示符并不工作。你必须通过 winpty vue.cmd create hello-world 启动这个命令。不过&#xff0c;如果你仍想使用 vue crea…

发布会前准备新闻通稿的重要性,为什么媒体不会原稿发布报道?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体 胡老师。 最近有宣传的小伙伴问胡老师&#xff0c;为什么我们精心准备的新闻通稿&#xff0c;媒体没有按照稿子发布呢&#xff1f;今天就与大家交流下这方面的经验。 一&#xff0c;发布会前准备新…

中断嵌套实验

使用汇编语言&#xff0c;要求&#xff1a; 外部中断1可以嵌套外部中断0 没有中断时&#xff0c;8个LED发光二极管以0.1s的速度闪烁。 有外部中断0时&#xff0c;8个LED发光二极管以0.1s的速度流水点亮。&#xff08;中断子程序0&#xff09; 有外部中断1时&#xff0c;会打断外…

还在为招生发愁?一文get中外合办院校招生技巧

生源&#xff0c;是任何一所高校的生存之本和生命线。因此&#xff0c;正确的招生策略&#xff0c;对于院校来说显得格外重要。 近几年&#xff0c;越来越多的家长和学生开始关注中外合办大学&#xff0c;随之而来的中外合办大学的生源竞争也越来越激烈。那么&#xff0c;有哪…

学好虚拟化,首先要学Linux

上次讲到了虚拟化的基础知识&#xff0c;比如虚拟化的应用、各个厂商都是通过何种技术路径来实现的等等&#xff0c;本篇想记录一下我学习到的CPU内存虚拟化和网络虚拟化相关知识&#xff0c;通过记录来制造反馈&#xff0c;让自己更有效地学习。需要注意的是&#xff0c;学习虚…

这 7个 AI 写作助手,太实用了

想象一下&#xff1a;你正在办公桌前为你的广告输入标题&#xff0c;但你突然思维阻塞并卡住了&#xff0c;可惜这时还没有神奇的软件可以帮助你想出点子。或许是有的&#xff1f; 2023 年&#xff0c;AI 写作工具似乎不可避免地会很快融入我们的工作流程中。现代知识工作者已…

政府大数据中心数据资源平台建设方案WORD2022

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除 1.1 项目建设内容 对于本次区级大数据资源平台的建设&#xff0c;将按照“总体规划一步到位&#xff0c;平台建设分步实施&#xff0c;数据赋能逐步升级”的原则&#xff0c;落…

理解什么是DTO?什么是AutoMapper?

什么是DTO? .Net DTO是一个对象&#xff0c;它定义了数据如何在网络上发送。它只用于发送和接收数据&#xff0c;不包含任何业务逻辑。使用DTO的原因有以下几个&#xff1a; 将服务层与数据库层分离隐藏客户端不需要查看的特定属性省略一些属性以减少有效负载大小处理嵌套对象…

【LeetCode: 233. 数字 1 的个数 | 暴力递归=>记忆化搜索=>动态规划 | 数位dp】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Term Suggester 中 suggest_mode 的三种模式missing、popular、always 的区别

1、Term Suggester term suggester 正如其名&#xff0c;只基于 tokenizer 之后的单个 term 去匹配建议词&#xff0c;并不会考虑多个term之间的关系 POST <index>/_search { "suggest": {"<suggest_name>": {"text": "<s…