“栈”与“队列”呢点事(一)

因为栈和队列放在一起学习更容易理解,因为本文将化繁为简,尽量通俗易懂的解释栈与队列。
化腐朽为神奇???不,好像不能。

定义

栈是一种操作受限的线性结构。举个比较典型的例子,叠一摞盘子。放盘子的时候,都是从下往上一个一个放;取的时候,也是从上往下一个一个地依次取,不能从中间任意抽出。这就是一种典型的‘栈’结构。它的思想就是先进后出,后进先出

注意:栈的插入和删除操作只允许栈顶(也就是末尾)进行。

栈又分顺序栈链式栈。基于数组实现的栈叫做顺序栈,基于链表实现的则叫链式栈。

栈的应用其实很广泛。比如表达式的转换和求值,函数调用,逆序输出,语法的检查(“()”“[]”“{}”“<>”这些成对出现的符号是否完整)回溯,递归,深度优先搜索等,业务上比如浏览器的前进后退。

栈的基本操作:

  • 入栈(push):将数据放入栈顶,栈顶top指针加一。
  • 出栈(pop):将栈顶数据数据输出,栈顶数据减一。

队列

队列也是一种操作受限的线性结构。它的思想也正好和栈相反--先进先出,后进后出。举个例子,排队买票,先来的先买,后来的人只能站末尾,并且不允许插队。队列的概念相对栈来讲,更好理解。

注意:队列它是只允许在首端删除,尾端插入。

队列分为顺序队列链式队列,循环队列,阻塞队列,并发队列。同样,用数组实现的队列叫作顺序队列,用链表实现的队列叫作链式队列。循环队列则是一个环状。至于阻塞队列它其实就是在普通队列的基础上增加了阻塞的操作(生产者-消费者模型)。并发队列是基于线程安全为大前提的,也就是每一次的入队出队操作都加了锁(利用 CAS 原子操作,可以实现非常高效的并发队列)。

队列的应用也更加的广泛。比如循环队列、阻塞队列、并发队列。它们在很多偏底层系统、框架、中间件的开发中,起着关键性的作用。比如高性能队列 Disruptor、Linux 环形缓存,都用到了循环并发队列;Java concurrent 并发包利用 ArrayBlockingQueue 来实现公平锁;消息队列(activeMQ、rabbitMQ、rocketMQ、zeroMQ,kafka等等)就更不用提了,还有线程池等等。

队列的基本操作

  • 入队(enqueue):将数据放入尾端,数据大小加一。
  • 出队(dequeue):将数据从首端取出,数据大小减一。

思考

关于堆栈

一般说栈的时候,也会提一下堆,但是要注意内存中的堆栈和数据结构堆栈不是一个概念,可以说内存中的堆栈是真实存在的物理区,数据结构中的堆栈是抽象的数据存储结构。

内存空间在逻辑上分为三部分:

代码区:存储方法体的二进制代码。高级调度(作业调度)、中级调度(内存调度)、低级调度(进程调度)控制代码区执行代码的切换。

静态数据区:存储全局变量、静态变量、常量,常量包括final修饰的常量和String常量。系统自动分配和回收。

和动态数据区:

动态数据区又分为栈区和堆区。

栈区:存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。
堆区:new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。

关于队列

使用队列的时候要注意

  • 队列为空
  • 队列只有一个元素,即头尾指针都指向空
  • 初始化队列时,分配空间后不要忘记将头为指针置空

队列应用于有限的资源池的时候,用于排队请求,比如数据库连接池,线程池等。实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队。

浅谈一下消息队列

有这么一种场景:小王到M记点餐之后,服务员给了他一个号牌,并让他在柜台桌子前方等待叫号取餐。每个人都按照自己付款拿到的号牌顺序排队等叫号。即使店里人再多,也不会显得没有秩序。

在上述场景中,柜台其实就充当了一个消息队列(Message Queue)。小王等生产者把订餐的消息发送到柜台即消息队列里,又从其中取了餐即消费了消息,可以说这就是消息队列的一个完整走向——消息被发送到队列中,又成功被消费者消费。“消息队列”是在消息的传输过程中保存消息的容器,队列的主要目的是提供路由并保证消息的传递。如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。

一般来说,消息队列是一种异步的服务间通信方式,是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。

扩展

下一篇则关于栈的具体实现和解析。

end

您的点赞和关注是对我最大的支持,谢谢!


你可能感兴趣的:(“栈”与“队列”呢点事(一))