算法与数据结构-队列

news2024/12/26 21:39:56

文章目录

  • 什么是队列
    • 队列和栈的区别
  • 队列的类型
    • 顺序队列
    • 链式队列
    • 循环队列
    • 阻塞队列
    • 并发队列
  • 总结


什么是队列

  队列跟栈一样,也是一种操作受限的线性表数据结构。不过,队列是先进者先出。

队列和栈的区别

  栈只支持两个基本操作:入栈 push()和出栈 pop()。队列跟栈非常相似,支持的操作也很有限,最基本的操作也是两个:入队 enqueue(),放一个数据到队列尾部;出队 dequeue(),从队列头部取一个元素。
在这里插入图片描述
  队列的概念很好理解,基本操作也很容易掌握。作为一种非常基础的数据结构,队列的应用也非常广泛,特别是一些具有某些额外特性的队列,比如循环队列、阻塞队列、并发队列。它们在很多偏底层系统、框架、中间件的开发中,起着关键性的作用。比如高性能队列 Disruptor、Linux 环形缓存,都用到了循环并发队列;Java concurrent 并发包利用 ArrayBlockingQueue 来实现公平锁等。

队列的类型

  跟栈一样,队列可以用数组来实现,也可以用链表来实现。用数组实现的栈叫作顺序栈,用链表实现的栈叫作链式栈。同样,用数组实现的队列叫作顺序队列,用链表实现的队列叫作链式队列。

顺序队列

public class ArrayQueue<T> {
    /**
     * 存储数据的数组
     */
    private T[] tArr;
    /**
     * 头坐标
     */
    private int head = 0;
    /**
     * 尾坐标
     */
    private int tail = -1;
    /**
     * 队列容量
     */
    @Getter
    private int size = 0;

    /**
     * 构造函数
     */
    public ArrayQueue(int arrLength) {
        tArr = (T[]) new Object[arrLength];
    }

    /**
     * 入队,线程不安全
     */
    public boolean offer(T t) {
        // 队列是否已满
        if (size == tArr.length) {
            return false;
        }

        // 尾是否已到数组最后,到达最后则移动
        if (tail == tArr.length - 1) {
            // 移动数组
            for (int i = 0; i < size; i++) {
                tArr[i] = tArr[head + i];
            }
            // 重设头尾坐标
            head = 0;
            tail = tail - size;
        }

        // 设置值
        tail++;
        tArr[tail] = t;
        size++;
        return true;
    }

    /**
     * 出队,线程不安全
     */
    public T take() {
        // 队列是否为空
        if (size == 0) {
            return null;
        }

        // 取值
        T t = tArr[head];
        head++;
        size--;

        return t;
    }
}

  从代码中我们看到,当队列的 tail 指针移动到数组的最右边后,如果有新的数据入队,我们可以将 head 到 tail 之间的数据,整体搬移到数组中 0 到 size(队列大小) 的位置。图示如下:
在这里插入图片描述

链式队列

public class LinkedQueue<T> {
    /**
     * 队列头部节点
     */
    private QueueNode<T> headNode = null;
    /**
     * 队列尾部节点
     */
    private QueueNode<T> tailNode = null;

    /**
     * 入队,线程不安全
     */
    public boolean offer(T t) {
        // 定义新节点
        QueueNode<T> newNode = new QueueNode<>();
        newNode.setData(t);

        // 对头为空时设置为新节点
        if (headNode == null) {
            headNode = newNode;
        }

        // 队尾非空时,设置其下一节点为新节点
        if (tailNode != null) {
            tailNode.setNextNode(newNode);
        }

        // 重设队尾节点
        tailNode = newNode;
        return true;
    }

    /**
     * 出队,线程不安全
     */
    public T take() {
        // 队列为空
        if (headNode == null) {
            return null;
        }

        // 获取当前节点的数据
        T data = headNode.getData();

        // 取上一节点设置为栈顶
        headNode = headNode.getNextNode();
        return data;
    }


    @Data
    private class QueueNode<T> {
        /**
         * 数据
         */
        private T data;
        /**
         * 上一个节点
         */
        private QueueNode<T> nextNode = null;
    }
}

  基于链表的实现,我们同样需要两个指针:head 指针和 tail 指针。它们分别指向链表的第一个结点和最后一个结点。如图所示,入队时,tail->next= new_node, tail = tail->next;出队时,head = head->next。我们图示如下:
在这里插入图片描述

循环队列

public class CircleQueue<T> {
    /**
     * 存储数据的数组
     */
    private T[] tArr;
    /**
     * 头坐标
     */
    private int head = 0;
    /**
     * 尾坐标
     */
    private int tail = -1;
    /**
     * 队列容量
     */
    @Getter
    private int size = 0;

    /**
     * 构造函数
     */
    public CircleQueue(int arrLength) {
        tArr = (T[]) new Object[arrLength];
    }

    /**
     * 入队,线程不安全
     */
    public boolean offer(T t) {
        // 队列是否已满
        if (size == tArr.length) {
            return false;
        }

        // 尾是否已到数组最后,到达最后则移动
        int newTail = (tail + 1) % tArr.length;

        // 设置值
        tArr[newTail] = t;
        tail = newTail;
        size++;
        return true;
    }

    /**
     * 出队,线程不安全
     */
    public T take() {
        // 队列是否为空
        if (size == 0) {
            return null;
        }

        // 取值
        T t = tArr[head];
        head = head + 1 % tArr.length;
        size--;

        return t;
    }
}

  循环队列,顾名思义,它长得像一个环。原本数组是有头有尾的,是一条直线。现在我们把首尾相连,扳成了一个环。我画了一张图,你可以直观地感受一下。
在这里插入图片描述

  我们可以发现,图中这个队列的大小为 8,当前 head=4,tail=6。当有一个新的元素 a 入队时,我们放入下标为 7 的位置,把 tail 更新为 7。当再有一个元素 b 入队时,我们将 b 放入下标为 0 的位置,然后 tail 更新为0。

  从上面的图中我们可以看到,队列为空的条件是head = tail ,而队列满的条件是(tail + 1) = head,当tail + 1 > 8 时,tail + 1 = 0。而这个操作可以用(tail + 1)对 8 取模来完成,即队列满的条件是 (tail + 1) % 8 = head。

阻塞队列

  阻塞队列其实就是在队列基础上增加了阻塞操作。简单来说,就是在队列为空的时候,从队头取数据会被阻塞。因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。
在这里插入图片描述

并发队列

  线程安全的队列我们叫作并发队列。最简单直接的实现方式是直接在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或者取操作。实际上,基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。在实战篇讲 Disruptor 的时候,我会再详细讲并发队列的应用。

总结

  队列最大的特点就是先进先出,主要的两个操作是入队和出队。跟栈一样,它既可以用数组来实现,也可以用链表来实现。用数组实现的叫顺序队列,用链表实现的叫链式队列。特别是长得像一个环的循环队列。在数组实现队列的时候,会有数据搬移操作,要想解决数据搬移的问题,我们就需要像环一样的循环队列。

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

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

相关文章

【Kafka】Kafka consumer lag 为负数

前言 最近对Kafka 集群部署了 Kafka_exporter 监控&#xff0c;并集成了 granfana 图标展示。 发现 Consumer Group Lag 有时候为负数。 于是进行一番查询&#xff0c;并总结整理下。 具体情形 从下图可以看出&#xff0c;consumer group 值有时候出现负数的情况。 具体原…

Python 使用 pyc 解决明文密钥问题

文章目录 前言1. pyc 介绍2. py 代码编译2.1 使用命令行编译2.2 使用代码编译 3. 避免名为密钥案例3.1 创建密钥存储代码文件3.2 编译密钥代码3.3 调用密钥代码 前言 写代码过程中&#xff0c;可能遇到一些敏感信息不想明文暴露在代码中的情况&#xff0c;本篇文章介绍使用 py…

多tab之间的sessionStorage能不能直接共享访问?

首先&#xff0c;先理解下一个页面的sessionStorage是怎么回事。 1、当前A页面有sessionStorage值为123&#xff0c;我F5强刷页面&#xff0c;sessionStorage值还在不在&#xff1f; 答&#xff1a;在。 2、当前A页面有sessionStorage值为123&#xff0c;我复制A页面地址在浏览…

浏览器console发送get或post请求

浏览器console发送get或post请求 get请求 var url "http://******:8080/base/testapi/testcurl?urlhttps%3A%2F%2Fwww.baidu.com%2F"; var xhr new XMLHttpRequest(); xhr.open("GET", url, true); xhr.setRequestHeader("Content-Type", &…

RPG++——游戏编辑器的开发

完整资料进入【数字空间】查看——baidu搜索"writebug" 随着当下电子设备的普及以及人们对娱乐需求的上升&#xff0c;电子游戏逐渐走进千家万户。RPG&#xff08;角色扮演&#xff09;游戏作为最经典的游戏种类之一&#xff0c;因其游戏形式多样&#xff0c;自由度…

矢量数据库对比和选择指南

矢量数据库是为实现高维矢量数据的高效存储、检索和相似性搜索而设计的。使用一种称为嵌入的过程&#xff0c;将向量数据表示为一个连续的、有意义的高维向量。 本文将研究存储/检索向量数据和执行相似性搜索的实用方法&#xff0c;在我们深入研究之前&#xff0c;首先先介绍矢…

改进 Elastic Stack 中的信息检索:提高搜索相关性的步骤

作者&#xff1a;Grgoire Corbire, Quentin Herreros, Thomas Veasey 自 8.0 和用于文本嵌入的第三方自然语言处理 (NLP) 模型发布以来&#xff0c;Elastic Stack 的用户可以访问各种模型来嵌入其文本文档并使用矢量搜索执行基于查询的信息检索。 考虑到所有这些组件及其参数…

Rust 第二天---Rust基础总结

之前已经配置好了Rust的环境,那学习一门语言最开始就是去掌握了解它的基本语法.其实Rust的语法和大多编程语言没什么差别,熟悉C的应该很容易上手,所以今天就快速过一遍基础. 1. 变量与常量 变量应该是编程中最常用到的,但是Rust与其他语言不同的是在声明变量的时候必须说明这…

vue最强table vxe-table 虚拟滚动列表 前端导出

最近遇到个问题。后台一次性返回2万条列表数据。 并且需求要求所有数据必须全部展示&#xff0c;不能做假分页&#xff08;不能优化了&#xff09;。 这些数据的直接来源就是CS客户端。 他们做CS客户端就是一次性加载几万条数据不分页&#xff08;说这是客户的要求&#xff…

android_mars老师_定位_获取最佳的provider

结果展示 ManiActivity package com.example.locationmanager2;import androidx.appcompat.app.AppCompatActivity;import android.content.Context; import android.location.Criteria; import android.location.LocationManager; import android.os.Bundle; import android.…

开放式耳机对耳朵伤害大吗?开放式耳机是什么意思?

​今天来跟大家一起聊聊&#xff0c;开放式耳机对耳朵伤害大不大&#xff0c;还有就是开放式耳机到底是什么类型的耳机&#xff0c;且开放式耳机有哪些比较好用的... 开放式耳机对耳朵伤害大吗&#xff1f; 开放式耳机对耳机的伤害是比较小的&#xff0c;传统入耳式耳机佩戴久了…

【Linux实验】将个人主页上传到服务器

一、实验目的 l 网页制作&#xff1b; l 熟悉 Linux FTP or scp 拷贝&#xff1b; l 熟悉 apache 权限配置。 二、实验内容 l 创建个人主页&#xff1b; l 用 FTP 或 scp 上传服务器&#xff1b; l chmod 设置目录权限&#xff1b; l Web 访问。 三、实验代码 略…

ModaHub魔搭社区:Milvus向量数据库最权威全面的百科

目录 什么是 Milvus&#xff1f; Milvus 向量数据库专为向量查询与检索设计&#xff0c;能够为万亿级向量数据建立索引。与现有的主要用作处理结构化数据的关系型数据库不同&#xff0c;Milvus 在底层设计上就是为了处理由各种非结构化数据转换而来的向量而生。 产品版本 强…

进行EE存储时需要关中断

一、调度中执行存储 在task调度里执行存储任务&#xff0c;在存储之前必须关掉中断&#xff0c;存储之后再打开中断&#xff0c;否则将会影响存储。以下两个函数分别执行挂起所有嵌套的中断和恢复所有嵌套的中断。 二、下电存储 在下电存储之前也需要使EcuM Shutdown或者Shut…

彻底掌握IDEA Debug技巧让你的开发不受任何阻挠

高效开发&#xff1a;IntelliJ IDEA Debug技巧 Debug用来追踪代码的运行流程&#xff0c;通常在程序运行过程中出现异常&#xff0c;启用Debug模式可以分析定位异常发生的位置&#xff0c;以及在运行过程中参数的变化。通常我们也可以启用Debug模式来跟踪代码的运行流程去学习…

3-Spring cloud之搭建Ribbon负载均衡——服务器上实操(上)

3-Spring cloud之搭建Ribbon负载均衡——服务器上实操&#xff08;上&#xff09; 1. 前言2. ribbon整合eureka入门2.1 修改相关配置2.1.1 修改服务消费者pom&#xff0c;引入ribbon相关依赖2.1.2 修改服务消费者yml&#xff0c;将客户端注册进eureka服务列表内2.1.3 修改配置类…

Python爬虫爬取双色球开奖结果源码

使用Python编写双色球爬虫&#xff1a;解析彩票数据 引言&#xff1a; 在数字化时代&#xff0c;彩票已经成为人们休闲娱乐的一种方式。而双色球无疑是最受欢迎的彩票之一&#xff0c;它的中奖概率和巨额奖金吸引了无数人的注意。在本文中&#xff0c;我们将介绍如何使用Python…

华为OD机试真题 Python 实现【知识图谱新词挖掘1】【2023Q1 100分】

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、Python算法源码六、效果展示1、输入2、输出3、说明 一、题目描述 小华负责公司知识图谱产品&#xff0c;现在要通过新词挖掘完善知识图谱。 新词挖掘: 给出一个待挖掘文本内容字符串Content和一个词的字符串word&am…

apple pencil二代平替笔哪个好用?好用的苹果笔推荐

自从ipad和其他的平板电脑都搭配上了电容笔以后&#xff0c;电容笔很好地取代了我们的手指&#xff0c;书写的效率就大大提升了&#xff0c;但由于苹果原装电容笔的价格不够人性化&#xff0c;一直高居不下给普通人带来了很大的负担&#xff0c;特别是对于学生们来说&#xff0…

6个提高图片分辨率的在线网站,真的超级实用!

在日常生活中&#xff0c;我们经常会遇到保存的图片模糊不清的情况。有时候这可能是由于保存方式不当&#xff0c;有时候则是由于图片在上传至网站时被压缩所致&#xff0c;导致画质和分辨率下降。 那么&#xff0c;有没有办法可以提高图片的分辨率和画质呢&#xff1f;事实上…