关于堆栈的实验
今天
的数据结构课程布置了作业,按照以往一贯的行事风格当然是要在系统精细地了解了整个知识结构的前提下再完美地完成它——
其实就是为自己不想快速思考找个理由而已。
那么首先来复习一下定义吧!
翻书中……
翻书实在是太麻烦了还是求助互联网吧。
所谓的栈,
其实就是一种容器适配器,通俗地来讲是一种储存结构,特点就是一句话,先进先出。
在学习中主要是用c++类来实现的,并且有诸多的功能来实现对应的函数操作。
在栈这个结构中,元素是从特定容器的最后面(但是是栈容器的“栈顶”)弹出或者压入的(这两个词用得也很有意思)。
总体来说,栈这个结构应该支持以下几个功能:empty(判定栈是不是空的),size(返回栈的容量大小),back(栈顶元素),push(压入),pop(弹出)这些操作。
然后
我纠结的就是从大一的时候就没懂而且没搞明白的问题——所谓的堆、堆栈、栈,究竟是个什么东东?
其实大一的时候没弄懂也没去搞懂,但是到了大二就不行了——你都学到这儿了……而且据说在去公司面试的时候甚至都会问到这些东西!
那还了得(瞬间甩手爆炸)!
认真学习学习……
那么,什么是堆?
看过了网上的定义仍然是晕乎乎,但是梳理了一下之后,总结如下:
首先,完全二叉树明白吧。
嗯,就是一个叶子都没掉的二叉树,一生二二生四四生八那种……
然后,你要将这个二叉树存储在对应的数组之中。
这就比较难受了,因为你完全不知道这个蛋疼的顺序是应该如何存储在对应的空间中的。
到底应该是满足怎样的顺序呢?
其实我们可以转换一下对应的思路,其实我们要做的并非要将对应的二叉树完全存储在一个数组的列表之中,而是要实现对应的结构,因此,与其要纠结“堆这种二叉树应该怎样唯一地映射到数组中”这种问题,不妨反过来想一想,即什么样的数组能够代表一个堆就可以了。
那么规律其实很简单,
首先,对于一个完全二叉树来说,一个节点n的两个子节点,那就应该是2n和2n+1。
其实我离散数学学得很是垃圾,但是这么一想,1的是2和3,3的是6和7,正好是二叉树中的那几个,没毛病!
那么问题就简单了,虽然很多时候数学表达式令人深恶痛绝,可是数学实在是个好东东,于是对应数组的规律,如是而已——
第n个总是大于第2n和第2n+1个。
看看我是多么的人性化甚至都没用an这种奇怪的表达。
一旦你有了这样的表达,那么将其抽象和反为一个切实的二叉树就很是简单了。
据说堆这个东东是用来在程序运行的时候申请空间的,具体怎样就……关我鸟事。
栈呢?
实际上栈的结构那可真是没啥可说的,因为就是先进后出后进先出嘛,有什么可研究的。
资料中说是“一种运算受限的线性表”,是是是,当然了官方的定义看起来就是高大上话说什么运算受限不就是功能不足吗。
当然了,在学习过计算机系统之后也有了解到,这个所谓的栈其实在操作系统建立某个进程和线程时建立的存储区域。至于怎么建立的,关我鸟事
那么堆栈呢?
之所以我一直习惯用堆栈这个词语,没有别的,就是因为它字儿多。
原来是真的没什么区别啊!
其实也是有点区别的,但是妨碍不大,就像是我之前所说的,堆的结构特点是前后父子代拥有一种连带的大小关系,而所谓的栈只是确定了你不能先进先出而已,一个是结构特点一个是操作特点,根本没冲突啊。
这就不是你不能建立一个狗屁“队栈”的问题了,因为你没办法将两者组合在一起,这就好像是你可以要一个抹茶冰淇淋但是不能要一个奶昔冰淇淋一样
所以,结合了两者的特点,不再是随机的结构而是满足了堆的要求,不是随意操作而是后进先出,这就是堆栈。
既然没啥区别我就接着用呗,又不会怀孕。
那么接下来(其实什么也没干)就是做作业的时刻了。
这一次的作业
是一个
“给定一个字符串读取其中的运算符和数字输出运算的结果”
的问题
我:
好吧,其实细细想来也没有什么过分的,遇到数字读数字遇到符号读符号,那里不会点哪里,分两个地方放起来然后再进行运算,没毛病。
但是紧接着有个毛病——
如何确定运算顺序?如果一个加号后面跟了个乘号而你读取的时候根本不知道?
默认一个符号是需要两个运算符号,但是如果那是一个负数怎么办?
甚至如果正号他还要写出来?!
括号怎么办?
多位数呢?
小数呢?
于是我想到,用顺序读取然后运算的方法是绝对不可取的,但是如果全部读出来再进行运算,那也是不可以的,绝对不行,那么方法是什么?
看来是要用到后进先出了。
(放屁要不给你布置这个作业干嘛!)
那么整体思路如下:
首先,要确立两个堆栈,一个是符号一个是数字。
注意是两个堆栈。
首先最根本的操作,那一定就是读,每次读一个,但是注意了,并不是遇到一个字符中的符号紧接着就读进去,而是要读到一个字符之后,先进行对应的操作,然后再将这一个压入。
那么是什么操作呢?
对应的解释如下:
如果是加减,注意在所有的符号之中,这是两个最为低级的,而且由于是从后向前读写的,那么就要将前面所有的加减乘除都给执行了。
这是一个运算优先的问题。
如果是乘除,那么出于优先性的问题,自然是要选择将前面所有的乘除都给运算了。
如果是右括号,注意,实际上和左括号一样,这两兄弟是运算优先级别最低的,所以没什么好说直到你能读到左括号为止,全部运算!
而且这一步之后你会发现很爽的一点就是,你实际上是将数字堆栈中的很多个数字全部浓缩成一个了
如果是左括号,那么就将直接压入,没废话。
当然了,在这样的随读随写之中,必定是不能够将全部数字都运算完的(一般情况除非你的式子最外面就是一个括号),所以最后的最后,直到数组元素青空,都需要不断地进行运算,这就算是了了
就这么样?
当然不是。
比如说多位数的话需要设置一个变量,如果变量为真那么就将前面一个数字乘以10加上此数字,好比说负号的话依据元素个数和前面是否是括号和运算数字数组是否已经成空来判定其是否为负号,好比说balabala……
功能和蛋疼的问题是永无止尽的,能过检查,就行。
脑细胞死亡,骚身段重生,拜拜了您呐。
活过了,喝点茶休息休息才是正事儿。