0
点赞
收藏
分享

微信扫一扫

数据结构与算法之美笔记(二) 栈 队列 递归

我是小瘦子哟 2022-01-30 阅读 120
数据结构

先进后出
从栈的操作特性上来看,栈是一种“操作受限”的线性表,只允许在一端插入和删除数据。
虽然从功能上来说,数组或链表确实可以替代栈,但是特定的数据结构是对特定场景的抽象


栈在函数调用中的应用
操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构,用来存储函数调用时的临时变量。每进入一个函数,就会将临时变量作为一个栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈。

然后评论区的一条评论:
内存中的堆栈和数据结构堆栈不是一个概念,可以说内存中的堆栈是真实存在的物理区数据结构中的堆栈抽象数据存储结构。
内存空间在逻辑上分为三部分:代码区、静态数据区和动态数据区,动态数据区又分为栈区和堆区
代码区:存储方法体的二进制代码。高级调度(作业调度)、中级调度(内存调度)、低级调度(进程调度)控制代码区执行代码的切换。
静态数据区:存储全局变量、静态变量、常量,常量包括final修饰的常量和String常量。系统自动分配和回收。
栈区:存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。
堆区:new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。

队列

先进先出
相比较栈,队列也是操作受限,只有入队和出队操作

对于非循环顺序队列,一旦tail指针到达数组末尾,就需要进行数据搬移,即将head ~ tail之间的数据搬运到0 ~ head-tail的区域。根据均摊时间复杂度的计算方法易知时间复杂度也是O(1)
使用循环队列就可以减少这种搬运操作


阻塞队列
在这里插入图片描述
以下参考自博客:生产者消费者模型
生产者消费者模型
产生数据的模块:生产者;
处理数据的模块:消费者;
生产者和消费者之间的中介:缓冲区

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力,。这个阻塞队列就是用来给生产者和消费者解耦的。

在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。

生产者/消费者模型的优点
1、解耦,即降低生产者和消费者之间的依赖关系。
例如上述写信的例子,如果不使用邮筒(也就是缓区),你必须得把信直接交给邮递员。有同学会说,直接给邮递员不是挺简单的嘛?其实不简单,你必须得认识谁是邮递员,才能把信给他(光凭身上穿的制服,万一有人假冒,就惨了 )。这就产生和你和邮递员之间的依赖(相当于生产者和消费者的强耦合)。万一哪天邮递员换人了,你还要重新认识一下(相当于消费者变化导致修改生产者代码)。而邮筒相对来说比较固定,你依赖它的成本就比较低(相当于和缓冲区之间的弱耦合)。

2、支持并发,即生产者和消费者可以是两个独立的并发主体,互不干扰的运行。
从寄信的例子来看。如果没有邮筒,你得拿着信傻站在路口等邮递员过来收(相当于生产者阻塞);又或者邮递员得挨家挨户问,谁要寄信(相当于消费者轮询)。不管是哪种方法,效率都比较低。

3、支持忙闲不均,如果制造数据的速度时快时慢,缓冲区可以对其进行适当缓冲。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。
为了充分复用,我们再拿寄信的例子来说事。假设邮递员一次只能带走1000封信。万一某次碰上情人节(也可能是圣诞节)送贺卡,需要寄出去的信超过1000封,这时候邮筒这个缓冲区就派上用场了。邮递员把来不及带走的信暂存在邮筒中,等下次过来时再拿走。


递归

对于递归:不去一层层深扒下去,即只考虑当前状态与当前子问题的关系,不去想孙问题


警惕堆栈溢出:可以声明一个全局变量来控制递归的深度,从而避免堆栈溢出。


避免重复计算
即记忆化或动态规划,空间换时间
在这里插入图片描述

举报

相关推荐

0 条评论