• 3.栈与队列


    栈(stack)

    • 特点:操作受限的线性表,只允许在一端插入和删除数据,后进先出

    • 顺序栈,入栈操作有两种情况:

      • 栈空间足够,那么直接入栈就行(定义不涉及内存搬移的入栈操作为simple-push操作,时间复杂度为O(1))

      • 栈空间不足,则此时需要申请一块更大的内存空间,并将栈中的所有数据(设大小为K)搬移至新的栈中,这里的时间复杂度为O(n),那么假设栈容量为K,入栈K个数据,一共需要K次数据搬移和K次simple-push,均摊下来入栈的时间复杂度为O(1)

      均摊时间复杂度一般都等于最好情况下时间复杂度,因为大多数情况下,入栈时间复杂度都是O(1),只有在个别情况下才会退化成O(n)。

    • 栈的应用

      • 函数调用栈

      • 表达式求值

      • 括号匹配

      • 浏览器页面进退

      • ···

    队列(Queue)

    • 特点:操作受限的线性表,先进先出

    • 操作

      • 入队:tail++

      • 出队:head++

    • 顺序队列,入队出队的操作会使head和tail不断向数组右边移动,当tail移动到最右边,即使数组中还有空闲空间也无法继续往队列中添加数据了

      • 优化1:入队出队时进行数据搬移,使得head始终对应数组下标为0的数据。这种做法入队出队时间复杂度都是O(n)

      • 优化2:实际上出队时不用进行数据搬移,只需将head移动至下一个数据,在tail到达数组最后一个下标时触发一次集中的搬移操作。入队出队时间复杂度为O(1)

      • 优化3:循环队列

        • 队空条件:tail == head

        • 队满条件:(tail + 1)% n == head

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

        • 入队:tail++

        • 出队:head++

    • 队列的应用:队列是一种很有用的数据结构,可以应用在任何有限资源池中,用于排队请求

      • 阻塞队列:队列为空时取数据、队列满时插入数据会被阻塞。使用阻塞队列能轻松实现一个“生产者-消费者模型”

      • 并发队列:在多线程情况下会有多个线程同时操作队列,此时需要一个线程安全的队列

        • 方案1:加锁,但锁粒度大时并发度会比较低

        • 方案2:CAS,基于数组的循环队列利用CAS可以实现非常高效的并发队列

    • 顺序队列和链式队列对排队请求的影响

      • 顺序队列(有界队列)大小有限,当线程池中排队请求超过队列大小时,接下来的请求会被拒绝。这种方式对响应时间敏感的系统来说更为合理。不过设置一个大小合理的队列也是非常有讲究的,队列太大导致等待请求过多,队列太小无法充分利用系统资源。

      • 链式队列(无界队列)支持无限排队,但可能导致过多的排队请求,请求处理的响应时间过长。对于响应时间敏感的系统,不适合使用这种方式。

  • 相关阅读:
    洛谷【P1109 学生分组】 题解
    卡特兰数
    并查集
    深度优先搜索DFS;递归
    【71】序列模型和注意力机制
    c/c++ 常用的几个安全函数
    win32 Ui 编程 收集
    vc获取特殊路径(SpecialFolder)
    std::map 自定义排序
    16-----BBS论坛
  • 原文地址:https://www.cnblogs.com/codespoon/p/13231180.html
Copyright © 2020-2023  润新知