数据结构——栈与队列

1.与向量和列表的关系

栈和队列都属于序列的特殊形式,如栈只能在一端插入和删除,这导致了它后插入先删除的特性(后进先出),同时插入删除也可以改个名push和pop,因为只能对端口操作,所以查找的名也改了一个top。但是排序、查找那些序列的接口都还能用,所以可以直接由向量或者是列表派生。

2.栈应用——递归函数

递归函数所占用的空间复杂度取决于递归深度,而不是递归实例总数(递归实例是指每个输入值不同的函数,用图来表示就是用指树的节点数,而递归深度是指树的高度),而递归的时间复杂度和n有关,不同递归实例不同,可用公式推导。

递归都可转为迭代,转为迭代在空间复杂度上会有改善。如n的阶乘,递归是O(n)空间复杂度,迭代只是O(1)。(迭代本质是重复一段代码,数值会有变化)

尾递归很容易用迭代实现。

3.栈应用——进制转换

简单用栈可以实现后进先出。

4.栈应用——括号匹配

对于只有一种括号的情况,只要顺序扫描,(入栈,)出栈,如果最后栈空则匹配。(这种情况都不需要用栈,直接一个数就可以了)

对于多种括号的情况,用栈也可以很好处理。

5.栈应用——栈混洗

栈混洗的结果数量,catalan(n)卡特兰数,这种数表示c(n)等于0到n-1的卷积?因为栈混洗可以将栈的首元素出栈前已经有多少个元素已经进入栈B中进行分类,一共n种情况,每种情况正好是从c(n-1)*c(0),c(n-2)*c(1),c(n-3)*c(2)……。下面这个图竖线可以表示的前者,横线表示后者。图中表示的是c(6)*c(1)的情况。

检测禁型:首先312肯定不是栈混洗,没有312的一定是栈混洗(可以证明?)在定理下可以用ijk实现O(n^3)的复杂度,进一步可以得到312形式的其中必会有j+1,i,j这种形式,用ij实现O(n^2)的复杂度

 但是注意到n个元素的栈混洗,等价于n对括号的匹配(二者的组合数,也自然相等),那么可以用括号匹配的算法在O(n)中完成。(其实不是括号匹配的算法,因为括号匹配的组合和栈混洗的结果一一对应,但是不匹配的不会对应,括号的有无数种,而n!-卡特兰数是有限数,可以模拟栈混洗的过程去实现算法)

6.栈应用——中缀表达式及逆波兰表达式

缀表达式(操作符在操作数间):利用操作数栈和操作符两个栈,利用操作符的优先级进行判别,可以很好的完成计算过程,本质来讲是一个由内到外,由局部到整体的计算过程,所以用栈比较合适。

逆波兰表达式(RPN):又称后缀表达式,操作符在操作数之后,从左至右遇到操作符即可执行,之间要用空格分隔(因为操作数可能会连在一起不像中缀表达式那样),求逆波兰表达式只要一个操作数栈即可(不用操作符栈是因为遇到一个操作符就可以操作,不需要储存

由中缀表达式转化为逆波兰表达式可以上述中缀表达式求值过程(操作数栈push时接入,操作符操作时接入)

总结:栈的本质还是储存,如果处理的数据具有某种局部的后进先出性,用栈是再好不过了。比如括号匹配问题,后进的括号先出,比如表达式问题,后进的操作数先出。

7.队列

也是一种受限制的序列。只能再序列尾插入,序列头删除(查找),接口enqueue,dequeue,front()。因为涉及到不同端的插入和删除,因此一般使用列表类派生,而不是向量。(这也是列表的一个重要用途

8.栈应用——直方图内最大矩形

为什么用栈比普通的算法要好:普通算法求S(r)(小于r高度的最大秩加1)每一个r都要对前面r个元素遍历,总的O(n^2)复杂度。而用栈的话每向前一步,都会把比当前元素大的秩从栈里面pop出去,直到栈顶小于当前元素。能这么做的原因是因为去除这些元素并不会影响后面S(r)的求解,因为这些元素既比当前元素小,又没当前元素靠后,当然是永无出头之日。所以从O(n^2)到O(n)的复杂度,优化的点是在于清楚了这些没必要比较的秩。

后续还可以从三次遍历变为一次遍历,关键点在于对t进行遍历,把以t为右边界的r全都勘查一遍大小,这样栈里呈现递增的趋势,因此正好是左边界,可以进行计算。

这种优化为什么要用到栈:因为是从内而外的搜寻。

9.steap和queap(stack与heap  queue与heap)

 steap中有两个栈,一个(S)存放数据,一个(P)存放最大值。有三个接口,一个取最大值,直接p.top,一个pop,同时对两个栈pop,还有一个是push,s直接push,p要push S的最大值,即p的top与新来元素的较大值。总的来说就是一个普通的栈加了可以取最大值的接口。

 queap中和steap含义类似,也有两个队列,一个存放数据,一个存放最大值。同时也有三个接口,其中取大值的接口以及dequeue接口语义与steap类似,但要实现这两个接口,enqueue接口就不如steap的push接口那么容易实现。要把p中小于e的全变成e,可能要O(n),而不能像steak那样在O(1)完成。

9.双栈当队

 利用两个栈可以实现队列,考虑复杂度的时候不能因为dequeue可能的时候要O(n)复杂度, 就认为每次都这样,O(n)的时候是把其他的元素转移时间也考虑在内,考虑一个完整的元素系列的复杂度也就O(n)的复杂度,一个元素平均两次出栈两次入栈,每个元素分摊是常数时间,而不能把那一次当作每个元素的分摊时间。这也是考虑分摊复杂度的意义所在,考虑一个序列完整的生命周期,避免把最坏当成平均了。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值