队列

作者: OOM_Killer | 来源:发表于2019-08-25 14:09 被阅读0次

如何理解队列

队列与栈做比较,就是队列是先进先出,队列本身就像一个管子一样。


队列

先进先出就是一个典型的队列。队列的应用十分广泛,特别是具有额外特性的队列,比如循环队列,阻塞队列,并发队列等,这些都是偏底层系统,框架,中间件的开发,都是有队列的身影,比如高性能的队列Disruptor、Linux环形缓存等。Java concurrent 并发包利用ArrayBlockingQueue 来实现公平锁等

如何实现一个队列

队列最基本的操作是 入队enqueue(),放一个数据到对尾;出队dequeue(),从队头取出一个元素。用数组实现的队列就是顺序队列,用链表实现的队列就是链式队列

顺序队列

一个最基础的顺序队列实现(使用golang)

type Queue struct {
    Items   []string  // 数组:items  数组大小:n
    Cnt     int
    Head    int       // Head 队头下标  Tail 队尾下标
    Tail    int
}

// 初始化一个大小为 capacity 的数组
func (q *Queue) Init(capacity int) {
    q.Items = make([]string,capacity)
    q.Cnt = capacity
    q.Head,q.Tail = 0,0
}

// 入队
func (q *Queue) Enqueue(item string) bool {
    if q.Tail == q.Cnt {  // Tail 到尾部了
        if q.Head == 0 {  // 真的没空间了
            return false
        }else {           // 数据搬移
            for i := q.Head;i < q.Tail; i++ {
                q.Items[i - q.Head] = q.Items[i]
            }
            q.Tail = q.Tail - q.Head
            q.Head = 0
        }

    }
    q.Items[q.Tail] = item
    q.Tail++
    return true
}

// 出队
func (q *Queue) Dequeue() (string,bool) {
    if q.Head == q.Tail {
        return "",false
    }
    ret := q.Items[q.Head]
    q.Items[q.Head] = ""
    q.Head++
    return ret,true
}

这种利用数组的队列是最基础的队列。

顺序队列的分支(循环队列)

在上面的例子中,在tail=n的时候会进行一次数组搬移的操作,这样的入队操作其实在性能上是有一定的影响的,如果使用了循环队列,那么就不会存在数据搬移动的操作了。


循环队列

也就是说当tail到数组的尾部时候,会将新的元素重新放进数组头(前提是队列的head指针已经不再指向数组头了)
其实这样的代码看起来十分的简单,但是想要写出没有bug的代码,需要注意两点的判断 队满的状态(tail == cnt),队空的状态 (head == tail)。
如上图的队满情况下,tail=3 , head =0 . 而 cnt = 4 。 总结规律就是(3+1)%4 =0, 也就是 (tail + 1)%cnt = head 就代表队满了。

上图的Full 明明 在 下标为3 的位置没有存放数据,这是因为循环队列就是会浪费一个数组的存储空间。仔细想一想就明白为什么了,注意还要判断队空呢

下面就是一个循环队列的 golang 代码

// 循环队列
type CircularQueue struct {
    Items []string
    Cnt   int
    Head  int
    Tail  int
}

func (cq *CircularQueue) Init(capacity int) {
    cq.Items = make([]string,capacity)
    cq.Cnt = capacity
    cq.Head,cq.Tail = 0,0
}

// 入队操作
func (cq *CircularQueue) EnQueue(item string) bool {
    // 判断是否队满
    if (cq.Tail + 1) % cq.Cnt == cq.Head {
        return false
    }
    cq.Items[cq.Tail] = item
    cq.Tail = (cq.Tail + 1) % cq.Cnt
    return true
}

// 出队操作
func (cq *CircularQueue) DeQueue() (string,bool) {
    // 判断是否队空
    if cq.Head == cq.Tail {
        return "", false
    }
    ret := cq.Items[cq.Head]
    cq.Head = (cq.Head + 1) % cq.Cnt
    return ret,true
}
实际的运用

在实际的运用中,队列的出境率身份的高,因为队列就是一个典型的生产者消费者模型,但是在高并发的场景下,在并发队列中,一定要在 EnQueue 和 DeQueue 上加锁,实际上 基于数组的循环队列,利用了CAS原子操作,可以实现非常高效的并发队列,循环队列比普通的并发队列和链式队列应用的更多。循环队列也更为广泛。

相关文章

  • 队列

    队列特性 对比队列和栈 基于数组的队列 对比队列学习循环队列 循环队列难点 阻塞队列 并发队列 应用:线程池中拒绝...

  • 队列

    文章结构 什么是队列 实现队列顺序队列链式队列循环队列 Java中的队列 1. 什么是队列 队列也是一种操作受限的...

  • iOS底层-- GCD源码分析(1)-- dispatch_qu

    手动目录认识队列队列的结构队列的产生主队列全局队列创建的队列管理队列 代码版本dispatch version :...

  • 队列,异步,同步,线程通俗理解

    一、队列 串行队列 并行队列 主队列(只在主线程执行的串行队列) 全局队列(系统的并行队列) 二、 任务(是否具有...

  • GCD基础总结一

    上代码~ 同步串行队列 同步并行队列 异步串行队列 异步并行队列 主队列同步 会卡住 主队列异步

  • OC多线程

    队列创建 线程与队列 队列线程间通信 队列组

  • GCD

    获得主队列 获得全局队列 串行队列 异步队列 同步队列 阻隔队列 (像栅栏一样 ) 例如 A -->栅栏 --...

  • 数据结构第三篇 队列

    队列的特性 前进先出。 我们来大致描述下进出队列的情况。 进队列 1 进队列现在队列是 12 进队列现在队列是 1...

  • 利用链表实现队列

    队列成员变量: 队列长度 队列头节点 队列尾节点队列方法: 队列包含元素个数 队列是否为空 进队操作 出队操作 d...

  • Git 常用操作命令(持续更新)

    当前更新到stash队列 查看stash队列 清空队列 删除某个队列

网友评论

      本文标题:队列

      本文链接:https://www.haomeiwen.com/subject/oojhectx.html