【数据结构】Java实现队列与循环队列

目录

1. 概念

 2. 队列的使用  

3. 自己动手实现队列

3.1 MyQueue接口

3.2 LinkedQueue类

3.3 入队列

3.4 出队列

3.5 获取队头元素

3.6 获取队列中有效元素个数与检测队列是否为空

3.7 toString方法

4. 整体实现

4.1 LinkedQueue类

4.2 Test类

4.3 测试结果

5. 循环队列

 6. 实现循环队列

 6.1 LoopQueue类

6.2 判断队列是否为满与是否为空&获取队列中有效元素的个数

6.3 入队列

6.4 出队列

6.5 获取队头元素

6.6 toString方法

7. 整体实现循环队列

7.1 LoopQueue类

7.2 Test类

 7.3 测试结果

8. 双端队列 (Deque)

8.1 双端队列的线性实现

8.2 双端队列的链式实现


1. 概念

队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)。

入队列:进行插入操作的一端称为队尾(Tail/Rear)

出队列:进行删除操作的一端称为队头(Head/Front)

 2. 队列的使用  

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

【注意】Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

public static void main(String[] args) {
        Queue<Integer> q = new LinkedList<>();
        q.offer(1);
        q.offer(2);
        q.offer(3);
        q.offer(4);
        q.offer(5); // 从队尾入队列
        System.out.println(q.size());
        System.out.println(q.peek()); // 获取队头元素
        q.poll();
        System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
        if(q.isEmpty()){
            System.out.println("队列空");
        }else{
            System.out.println(q.size());
        }
    }

3. 自己动手实现队列

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构 和 链式结构。思考下:队列的实现使用顺序结构还是链式结构好

3.1 MyQueue接口

public interface MyQueue<E> {
    void offer( E val);
    E pop();
    E peek();
    boolean isEmpy();
    int size();
}

3.2 LinkedQueue类

public class LinkedQueue<E> implements MyQueue<E> {
    private Node head;//头节点
    private Node tail;//尾节点

    private int size; // 车厢节点个数,保存的元素个数

    //车厢类的定义,车厢作为火车的内部类,对外部完全隐藏
    private class Node {
        E val;//保存的元素
        Node next;//下节车厢的位置
        Node(E val) {
            this.val = val;
        }
    }
}

3.3 入队列

public void offer(E val) {
        Node node = new Node(val);
        if (size == 0){
            head = node;
            tail = node;
        }
        tail.next = node;
        tail = node;
        size ++;
    }

3.4 出队列

public E pop() {
        if(size == 0){
            throw new IllegalArgumentException("Queue is empty,cannot pop!");
        }
        Node node = head;
        head = head.next;
        size --;
        return node.val;
    }

3.5 获取队头元素

public E peek() {
        if(size == 0){
            throw new IllegalArgumentException("jjj");
        }
        return head.val;
    }

3.6 获取队列中有效元素个数与检测队列是否为空

public boolean isEmpy() {
        return size == 0;
    }

    public int size() {
        return size;
    }

3.7 toString方法

public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("head [");
        for (Node x = head; x != null; x = x.next) {
            sb.append(x.val);
            if(x.next != null){
                sb.append("->");
            }
        }
        sb.append("] tail");
        return sb.toString();
    }

4. 整体实现

4.1 LinkedQueue类

import MyQueue;
public class LinkedQueue<E> implements MyQueue<E> {
    private Node head;//头节点
    private Node tail;

    private int size; // 车厢节点个数,保存的元素个数

    //车厢类的定义,车厢作为火车的内部类,对外部完全隐藏
    private class Node {
        E val;//保存的元素
        Node next;//下节车厢的位置
        Node(E val) {
            this.val = val;
        }
    }

    @Override
    public void offer(E val) {
        Node node = new Node(val);
        if (size == 0){
            head = node;
            tail = node;

        }
        tail.next = node;
        tail = node;
        size ++;

    }

    @Override
    public E pop() {
        if(size == 0){
            throw new IllegalArgumentException("jjj");
        }
        Node node = head;
        head = head.next;
        size --;
        return node.val;
    }

    @Override
    public E peek() {
        if(size == 0){
            throw new IllegalArgumentException("jjj");
        }
        return head.val;
    }

    @Override
    public boolean isEmpy() {
        return size == 0;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("head [");
        for (Node x = head; x != null; x = x.next) {
            sb.append(x.val);
            if(x.next != null){
                sb.append("->");
            }
        }
        sb.append("] tail");
        return sb.toString();
    }
}

4.2 Test类

public class LinkedQueueTest {
    public static void main(String[] args) {
        LinkedQueue<Integer> queue = new LinkedQueue<>();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        System.out.println(queue);
        System.out.println("出队列");
        queue.pop();
        System.out.println(queue);
        System.out.println("查看队头元素");
        System.out.println(queue.peek());
        System.out.println("获取队长以及判断队列是否为空");
        System.out.println(queue.size() + "  " + queue.isEmpy());

    }
}

4.3 测试结果

5. 循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。循环队列通常使用数组实现。

【数组下标循环的小技巧】 

在循环队列的数组中,下标进行取模操作 :

如何区分空与满

1. 通过添加 size 属性记录

2. 保留一个位置

3. 使用标记

 6. 实现循环队列

 6.1 LoopQueue类

public class LoopQueue<E> implements MyQueue<E> {

    private int head;
    private int tail;
    private int size;
//    size = (head - tail + data.length) % data.length;
    Object[] data;

    public LoopQueue() {
         data = new Object[10];
    }
    public LoopQueue(int size) {
         data = new Object[size + 1];
    }
}

6.2 判断队列是否为满与是否为空&获取队列中有效元素的个数

    private boolean isFull(){
        return (tail + 1) % data.length == head;
//        return size == data.length -1;
    }

    @Override
    public boolean isEmpy() {
//        return size == 0;
        return tail ==head;
    }

    @Override
    public int size() {
        return size;
    }

6.3 入队列

public void offer(E val) {
        if (isFull()){
            throw new NoSuchElementException("LoopQueue is full , cannot offer!!!");
        }
        data[tail] = val;
        tail = (tail + 1) % data.length;
        size++;
    }

6.4 出队列

public E pop() {
        if (isEmpy()){
            throw new IllegalArgumentException("LoopQueue is empty , cannot pop!!!");
        }
        E val = (E) data[head];
        head = (head + 1) % data.length;
        size --;
        return val;
    }

6.5 获取队头元素

public E peek() {
        if (isEmpy()){
            throw new IllegalArgumentException("LoopQueue is empty , cannot peek!!!");
        }

        return (E) data[head];
    }

6.6 toString方法

public String toString() {
        StringBuilder sb = new StringBuilder();
        int j = head;
        sb.append("fornt [");
        for (int i = 0; i < size; i++) {
            sb.append(data[j]);
            if(i != size - 1){
                sb.append(", ");
            }
            j = (j + 1) % data.length;
        }

        sb.append("] tail");
        return sb.toString();
    }

7. 整体实现循环队列

7.1 LoopQueue类

import queue.MyQueue;

import java.util.NoSuchElementException;

public class LoopQueue<E> implements MyQueue<E> {

    private int head;
    private int tail;
    private int size;

    Object[] data;

    public LoopQueue() {
         data = new Object[10];
    }
    public LoopQueue(int size) {
         data = new Object[size + 1];
    }

    @Override
    public void offer(E val) {
        if (isFull()){
            throw new NoSuchElementException("LoopQueue is full , cannot offer!!!");
        }
        data[tail] = val;
        tail = (tail + 1) % data.length;
        size++;
    }

    @Override
    public E pop() {
        if (isEmpy()){
            throw new IllegalArgumentException("LoopQueue is empty , cannot pop!!!");
        }
        E val = (E) data[head];
        head = (head + 1) % data.length;
        size --;
        return val;
    }

    @Override
    public E peek() {
        if (isEmpy()){
            throw new IllegalArgumentException("LoopQueue is empty , cannot peek!!!");
        }

        return (E) data[head];
    }
    private boolean isFull(){
        return (tail + 1) % data.length == head;
//        return size == data.length -1;
    }

    @Override
    public boolean isEmpy() {
//        return size == 0;
        return tail ==head;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        int j = head;
        sb.append("fornt [");
        for (int i = 0; i < size; i++) {
            sb.append(data[j]);
            if(i != size - 1){
                sb.append(", ");
            }
            j = (j + 1) % data.length;
        }

        sb.append("] tail");
        return sb.toString();
    }
}

7.2 Test类

public class LoopQueueTest {
    public static void main(String[] args) {
        LoopQueue<Integer> queue = new LoopQueue<>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        System.out.println(queue);
        System.out.println("出队列");
        queue.pop();
        System.out.println(queue);
        System.out.println("查看队头元素");
        System.out.println(queue.peek());
        System.out.println("两次出队列");
        queue.pop();
        queue.pop();
        System.out.println("查看队头元素");
        System.out.println(queue.peek());
        System.out.println(queue);
        System.out.println("获取队长以及判断队列是否为空");
        System.out.println(queue.size() + "  " + queue.isEmpy());
    }
}

 7.3 测试结果

8. 双端队列 (Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

【注意】Deque是一个接口,使用时必须创建LinkedList的对象。

在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

8.1 双端队列的线性实现

public static void main(String[] args) {
        // 可以使用双端队列作为栈使用,API
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(1);
        stack.push(3);
        stack.push(5);
        // 5
        System.out.println(stack.pop());
        // 3
        System.out.println(stack.peek());

    }

8.2 双端队列的链式实现

public static void main(String[] args) {
        // 可以使用双端队列作为栈使用,API
        Deque<Integer> stack = new LinkedList<>();
        stack.offer(1);
        stack.offer(3);
        stack.offer(5);
        // 1
        System.out.println(stack.poll());
        // 3
        System.out.println(stack.peek());
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

while实现1到100相加求和-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)

【案例2-7】while实现1到100相加求和 一、案例描述 考核知识点 while循环语句 练习目标 掌握while循环语句。 需求分析 1-100之间的数相加求和&#xff0c;本案例通过while循环语句来实现。 案例分析 效果如图2-10所示。1-100所有数的和 具体实现步骤如下&#xff1a; 在&l…

【进阶数据结构】——红黑树

&#x1f308;感谢阅读East-sunrise学习分享——[进阶数据结构]红黑树 博主水平有限&#xff0c;如有差错&#xff0c;欢迎斧正&#x1f64f;感谢有你 码字不易&#xff0c;若有收获&#xff0c;期待你的点赞关注&#x1f499;我们一起进步&#x1f680; &#x1f308;我们上一…

SpringCloud之 LoadBalancer和Feign负载均衡

文章目录LoadBalancer 负载均衡一、LoadBalanced 负载均衡&#x1f33d;①观察负载均衡现象&#x1f33d;②LoadBalanced 源码剖析二、自定义负载均衡三、OpenFeign 实现负载均衡&#x1f346;①添加依赖&#x1f346;②启动类添加 EnableFeignClients&#x1f346;③创建客户端…

MySQL的COUNT语句,竟然都能被面试官虐的这么惨!?

关于数据库中行数统计&#xff0c;无论是MySQL还是Oracle&#xff0c;都有一个函数可以使用&#xff0c;那就是COUNT 但是&#xff0c;就是这个常用的COUNT函数&#xff0c;却暗藏着很多玄机&#xff0c;尤其是在面试的时候&#xff0c;一不小心就会被虐。不信的话请尝试回答下…

一文了解Jackson注解@JsonFormat及失效解决

背景 项目中使用WRITE_DATES_AS_TIMESTAMPS: true转换日期格式为时间戳未生效。如下&#xff1a; spring:jackson:time-zone: Asia/Shanghaiserialization:WRITE_DATES_AS_TIMESTAMPS: true尝试是否关于时间的注解是否会生效&#xff0c;使用JsonForma和JsonFiled均失效。 常…

【Docker】CAdvisor+InfluxDB+Granfana容器监控

文章目录原生命令 docker stats容器监控3剑客CIGCAdvisorInfluxDBGranfanacompose容器编排&#xff0c;一套带走新建目录新建3件套组合的 docker-compose.yml检查配置&#xff0c;有问题才有输出 docker-compose config -q启动docker-compose文件 docker-compose up -d测试浏览…

HTML5 Canvas

HTML5 Canvas <canvas>元素是HTML5中的新元素&#xff0c;通过使用该元素&#xff0c;你可以在网页中绘制所需的图形。 标签定义图形&#xff0c;比如图表和其他图像&#xff0c;您必须使用脚本来绘制图形。在画布上&#xff08;Canvas&#xff09;画一个红色矩形&#…

Java基础知识之HashMap的使用

一、HashMap介绍 HashMap是Map接口的一个实现类&#xff08;HashMap实现了Map的接口&#xff09;&#xff0c;它具有Map的特点。HashMap的底层是哈希表结构。 Map是用于保存具有映射关系的数据集合&#xff0c;它具有双列存储的特点&#xff0c;即一次必须添加两个元素&#xf…

5个高清/4K视频素材网站,免费下载。

本期跟大家分享5个超好用的视频素材网站&#xff0c;4K质量&#xff0c;免费可商用。 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx 菜鸟图库主要提供设计素材为主&#xff0c;自媒体相关素材也很多&#xff0c;像商用图片、背景图、视频素材、音频素材都很齐…

算法刷题总结 (二) 回溯与深广搜算法

算法总结2 回溯与深广搜算法一、理解回溯算法1.1、回溯的概念1.2、回溯法的效率1.3、回溯法问题分类1.4、回溯法的做题步骤二、经典问题2.1、组合问题2.1.1、77. 组合 - 值不重复2.1.2、216.组合总和III - 值不重复且等于目标值2.1.3、17. 电话号码的字母组合 - 双层回溯2.1.4、…

KafKa知识汇总

前言 汇总相关知识 Kafka快速实战与基本原理详解

LeetCode:215. 数组中的第K个最大元素

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340;算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;215. 数组中的第K个最大元素 题目描述&#xff1a;给定整数数组nums和整…

Django 之 Cookie 和 Session

3. Cookie 和 Session 会话 因为 HTTP 协议是无状态的&#xff0c;每次浏览器请求 request都是无状态的&#xff0c;后台服务器无法识别当前请求与上一次请求及之后请求是否为同一用户。 对于静态网站来说无所谓&#xff08;所有用户看到的都是一样的&#xff09;&#xff0c…

【C++】引用详细解析

目录引用的概念引用的用法引用的特性常引用&#xff08;涉及权限的放大与缩小&#xff09;引用的使用场景**作参数****作返回值**正确使用引用返回传值、传引用效率比较引用和指针的区别引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0…

干货 | 开关电源的PCB布线设计技巧—如何降低EMI?

开关电源PCB排版是开发电源产品中的一个重要过程。许多情况下&#xff0c;一个在纸上设计得非常完美的电源可能在初次调试时无法正常工作&#xff0c;原因是该电源的PCB排版存在着许多问题。为了适应电子产品飞快的更新换代节奏&#xff0c;产品设计工程师更倾向于选择在市场上…

【Zblog建站】搭建属于自己的博客网站,并内网穿透实现公网访问

文章目录1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置4. 公网访问测试5. 结语1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕不开…

第十四届蓝桥杯三月真题刷题训练——第 20 天

目录 第 1 题&#xff1a;纸张尺寸 问题描述 输入格式 输出格式 样例输入1 样例输出1 样例输入 2 样例输出 2 运行限制 代码&#xff1a; 解析&#xff1a; 第 2 题&#xff1a;最大数字 第 3 题&#xff1a;全排列的价值_递推公式 问题描述 输入格式 输出格式…

【中级软件设计师】—操作系统考点总结篇(二)

【中级软件设计师】—操作系统考点总结篇&#xff08;二&#xff09; 1.操作系统概述 1.1操作系统的功能 1.2 特殊的操作系统 1.3 进程的概念和状态 进程与程序的区别&#xff1a; 进程是程序的一次执行过程&#xff0c;没有程序就没有进程 程序是一个静态的概念&#xff0c;…

flowable-ui

一、介绍 flowable-ui主要用于画流程图,之后再使用flowable整合Spring Boot。Flowable提供了一个web UI应用程序来演示和利用Flowable项目提供的功能。 flowable-ui的官网文档地址为: https://www.flowable.com/open-source/docs/bpmn/ch14-Applications/ 二、安装flowab…

Counterpoint发布颈戴式耳机市场报告,苹果Find My加持不易丢

根据市场调查机构 Counterpoint 公布的 2022 年第 4 季度报告&#xff0c;一加凭借着 20.2% 的市场占有率&#xff0c;成为印度市场最大的颈戴式耳机市场厂商。 一加以 20.2% 的市场占有率领先&#xff0c;boAt 以 16% 的份额位居第二。一加云耳 Z2 已经连续 3 个季度成为印度…