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可以实现非常高效的并发队列

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

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

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

你可能感兴趣的:(3.栈与队列)