数据结构--队列

一,如何理解“队列”

可以把队列想象成排队买票,先来的先买,后来的人只能站末尾,不允许插队。先进者先出,这就是典型的“队列”。 栈只支持两个基本操作:入栈push()和出栈pop()。队列跟栈非常相似,支持的操作也很有限,最基本的操作也是两个:入队enqueue(),放一个数据到 队列尾部;出队dequeue(),从队列头部取一个元素。如图:

队列也是一种操作受限的线性表数据结构。

队列作为一种非常基础的数据结果,队列的应用也非常广泛,特别是一些具有某些额外特性的队列,例如循环队列,阻塞队列,并发队列。

二,顺序队列和链式队列

1,顺序队列

顺序队列是有数据实现的。如下图:

实现顺序队列,需要两个指针:一个head指针,指向队头,一个是tail指针,指向队尾。用数组实现顺序队列时,需要注意,随着出入队列会队尾没有空间,数组中还有空间,这就需要我们做数据搬移。

根据之前的分析,可以知道出入对列的时间复杂度仍然是O(1)

2,链式队列

链式队列就是基于链表实现的,链式队列也需要两个指针:head指针和tail指针。分别指向链表的第一个结点和最后一个结点。如图:

三,特殊特性的队列

1,循环队列

循环队列,顾名思义,其像一个环,首尾相连。如下图:

图中队列的大小为8,当head=4,tail=7是,插入一个数据a放入下标为7的位置,tail此时tail=0,当再次插入b时,将b放入下标为0的位置,然后tail加1更新为1。

可以看出,循环队列成功的避免了数据搬移的操作。看起来不难理解,但是循环队列的代码实现难度要比非循环队列难的多,最关键的是,确定好队列空和队列慢的判定条件

对于用数组实现的非循环队列,队满的判断条件是tail==n,队空的判断条件是head==tail。对于循环队列,队列为空的判断条件仍然是head==tail,对队列为满的判断条件有点复杂,如下队列满情况的图:

图中队列满的情况是tail=3,head=4,n=8,对例举几个,总结下规律就是(3+1)%8=4。所以队列满的条件是(tail+1)%n=head

所以,当队列满时,图中的tail指向的位置实际上是没用存储数据的。所以循环队列会浪费一个数组的存储空间

2,阻塞队列和并发队列

队列这种数据结构很基础,平时的业务开发不大可能从零实现一个队列,甚至都不会直接用到。而一些具有特殊特性的队列应用却比较广泛,比如阻塞队列和并发队列。

阻塞队列其实就是在队列的基础上增加了阻塞操作。简单来说,就是队列为空时,从队头获取数据会被阻塞,直到队列中有数据才返回;如果队列满时,那么插入数据的操作会被阻塞,知道队列中的空闲位置后再插入数据,然后返回。如图下面的生产者和消费者模型:

这种基于阻塞队列实现的“生产者-消费者模型”,可以有效地协调生产和消费的速度。当“生产者”生产数据的速度过快,“消费者”来不及消费时,存储数据的队列 很快就会满了。这个时候,生产者就阻塞等待,直到“消费者”消费了数据,“生产者”才会被唤醒继续“生产”。 而且不仅如此,基于阻塞队列,我们还可以通过协调“生产者”和“消费者”的个数,来提高数据的处理效率,如下图:

在多线程情况下,会出现多个线程同时操作队列,这时候就会存在线程安全问题。这就需要并发队列来保存线程安全的队列。

线性安全的队列叫做并发队列。最简单的直接的实现方式是直接在出入队列方法上加锁。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值