算法与数据结构-队列

这篇具有很好参考价值的文章主要介绍了算法与数据结构-队列。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


什么是队列

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

队列和栈的区别

  栈只支持两个基本操作:入栈 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 的时候,我会再详细讲并发队列的应用。

总结

  队列最大的特点就是先进先出,主要的两个操作是入队和出队。跟栈一样,它既可以用数组来实现,也可以用链表来实现。用数组实现的叫顺序队列,用链表实现的叫链式队列。特别是长得像一个环的循环队列。在数组实现队列的时候,会有数据搬移操作,要想解决数据搬移的问题,我们就需要像环一样的循环队列。文章来源地址https://www.toymoban.com/news/detail-531015.html

到了这里,关于算法与数据结构-队列的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构与算法04:队列

    目录 什么是队列? 循环队列 双端队列 阻塞队列 队列的应用场景 每日一练 在 上一篇文章 中讲述了栈:先进后出就是栈,队列刚好相反, 先进先出的数据结构就是队列 ,还是拿纸箱子来举例:队列可以理解为一个没有底的纸箱子,往箱子里面放书,一本一本叠上去,但是

    2024年02月06日
    浏览(66)
  • 算法与数据结构(四)--队列

    队列是另一种特殊的表,这种表只在表首(也称为队首)进行删除操作,只在表尾进行插入操作。 队列的修改是按 先进先出 的规则进行的,所以队列又称为先进先出表,First In First Out,简称FIFO表。映射到生活中就是排队的队伍。 如示意图所示,a(1)就是队首元素,a(n)就是队

    2024年02月15日
    浏览(42)
  • 数据结构之栈、队列——算法与数据结构入门笔记(四)

    本文是算法与数据结构的学习笔记第四篇,将持续更新,欢迎小伙伴们阅读学习 。有不懂的或错误的地方,欢迎交流 栈是一种线性数据结构,其 只允许在固定的一端进行插入和删除 元素操作。进行数据插入和删除操作的一端称为栈顶 (Top), 另一端称为栈底 (Bottom)。栈中的

    2024年02月08日
    浏览(35)
  • Python数据结构与算法-数据结构(列表、栈、队列、链表)

    数据结构是指相互之间存在这一种或者多种关系的数据元素的集合和该集合中元素之间的关系组成。 简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。 比如:列表、集合与字典等都是一种数据结构。 N.Wirth:“程序=数据结构+算法” 数据结构按照其 逻辑结

    2024年02月08日
    浏览(45)
  • 【数据结构与算法】设计循环队列

      🧑‍🎓 个人主页:简 料   🏆 所属专栏:C++   🏆 个人社区:越努力越幸运社区   🏆 简       介: 简料简料,简单有料~在校大学生一枚,专注C/C++/GO的干货分享,立志成为您的好帮手 ~ C/C++学习路线 (点击解锁) ❤️ C语言阶段(已结束) ❤️ 数据结构与算法(ing) ❤

    2024年01月17日
    浏览(38)
  • 数据结构与算法:栈和队列

    栈是一种后入先出(LIFO)的线性逻辑存储结构。只允许在栈顶进行进出操作。 基本操作包括:入栈(push)/出栈(pop)/获取栈顶元素(peek)。 栈的实现主要有两种: 1. 数组实现,即顺序栈 2. 链表实现,即链式栈 无论是以数组还是以链表实现,入栈、出栈的时间复杂度都是

    2024年02月11日
    浏览(41)
  • 【数据结构与算法】队列的实现

    🌠 作者:@ 阿亮joy. 🎆 专栏:《数据结构与算法要啸着学》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先

    2024年02月07日
    浏览(42)
  • 数据结构与算法-双端队列

    Gitee上开源的数据结构与算法代码库:数据结构与算法Gitee代码库 双端队列、队列、栈对比 定义 特点 队列 一端删除(头)另一端添加(尾) First In First Out 栈 一端删除和添加(顶) Last In First Out 双端队列 两端都可以删除、添加 优先级队列 优先级高者先出队 延时队列 根据

    2024年02月13日
    浏览(37)
  • 【数据结构与算法】03 队列(顺序队列--循环队列--优先级队列--链队列)

    队列( queue )是一种常见的数据结构,它遵循先进先出(FIFO)的原则。队列可以理解为一个具有两个端点的线性数据结构,其中一个端点称为\\\"队尾\\\"(rear),用于插入新元素,另一个端点称为\\\"队首\\\"(front),用于移除元素。新元素被插入到队尾,而最早插入的元素总是在队

    2024年02月08日
    浏览(47)
  • 【Java数据结构 -- 队列:队列有关面试oj算法题】

    只允许在一端进行插入数据操作,在另一端进行删除数据操作得特殊线性表,队列是 先进先出 ,入队:进行插入操作得一端称为 队尾(rear) ,出队:进行删除操作的一端称为 队头(front) 。队列Queue是个接口, 底层通过链表实现的 。 boolean offer(E e) – 入队列 E poll() – 出队

    2024年01月25日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包