粉丝13.6万获赞70.8万

捉迷藏捉迷藏。你来找我来藏大树后小屋旁车尾后。快快藏。小汽车嘟嘟叫,不给躲不给藏后视镜看, 看不到车尾物没眼睛落车道,事故发难预防很尴尬。 朋友在快提醒不靠近远离他玩耍时都注意懂安全。笑哈哈。


捉迷藏捉迷藏。你来找我。来藏大树后小屋旁车尾后,快快藏,小汽车 嘟嘟叫,不给躲,不给藏后视镜看不到车尾部,没眼睛若车道 吉他玩耍时都注意懂安全。笑哈哈。

捉迷藏捉迷藏。你来找我。来藏大树后小屋旁车尾后,快快藏,小汽车嘟嘟叫,不给躲不给藏,后视镜看不到车尾雾没眼睛 远离他。玩耍时都注意,懂安全,笑哈哈。

这个视频我们来讲一下第三个限性的数据结构站,站就是 stack 这个词,我们在操作系统的学习中,或者是在编程 stack or flow 等,经常会听到他们在说的都是这一个站。站呢是一种受限的限行表, 它有什么?相对于数组啊,相对于链表啊,它有什么限制呢?它只允许在同一端进行插入和删除的操作, 所以说呢,就像是一摞盘子,我们无论是清洗盘子,还是说把洗好的盘子放上去,都是在这个顶部,我们不能到中间或者底部去抽,这样有可能会把这一摞盘子都给弄倒。 所以说呢,我们形象地说,站是一个后进先出,我越往后面放上去的,在拿的时候就越先拿出它来,所以叫做后进先出,也就是说 l i f o 这个词,同时呢,它只允许在一端操作,我们管这一端叫做站,顶就是在顶部进行操作, 在底部是封死的,最早进来的元素就在最底部,这种数据结构就叫做站,听起来就是站,好像也没什么作用吗?如果大家反应的快,发现数组还有链表都能实现这个效果, 我甚至都能在任意位置进行增删,更不用说只在一端进行增删了。那么为什么还要创造一个这种功能缩水版的站呢?其实这个限制就意味着更强的可控性, 当我们使用站的时候呢,来组我们的阅读者,告诉我们的使用者呢,这个数据集的操作的顺序是严格的的, l i f o 的 就是后进先出的,它相对于数组,相对于列表的不可控性的,它的可控性是非常强的, 借助这个特性呢,我们是能实现非常多看起来特别奇怪的问题的解决方案的。我们管把一个元素放入站中呢,就叫入站 push, 把一个元素它从站内拿出来,就叫做 pop 弹出站也叫出站, 不能在中间进行操作,只能对最顶部的进行放进去或者拿出来。 ok, 那 我们看一下我们的实践方式。前面我们也介绍介绍到,数组和列表是两种最最重要的数据结构,其实在 我们的系统底层呢,也只有这两种数据结构,剩下的数据结构呢,都是这两种数据结构的衍生,是对他们的进行的复杂的封装和组合。 那么呢,我们来看一下站啊站,因为也和数组和列表一样,都是一个限性的数据结构。什么叫限性的数据结构呢?就是除了头和尾的节点呢,在中间每个节点都只有一个前序,只有一个后来者, 比如说像这一个数组吧啊,中间这个节点他也只有一个,后面的他们首尾相连就组成了一条线,我们就叫做限性的数据结构。 那么就是说数组和列表明显都能把站给实现出来,但是在日常我们实际使用的时候,经常都是用数组来实现的,为什么呢? 因为我们也能看到啊,数组它是不需要额外的指征的,比较节省空间。再一个我们在介绍数组这种数据结构的时候也说过,他对缓存是非常友好的,借助程序的局部性原理,我在读 a 一 的时候,他有可能把 a 十六都给拿到了内存中了,非常的快。 再一个呢,常数也非常的小,虽然说他们都是 o 一 的操作啊,但是数组明显不需要进行节点的申请啊,节点的销毁这些操作是比列表快的。很多同学说,不对不对不对, 数组的插入和删除不是 o n 吗?怎么到你这就变成 o 一 了呢?这个其实也正是站的特性导致的,因为站只能在顶部进行操作,删,进行增加和删,删除 在不会在这一端。所以说对于数组来说呢,只要我数组的量足够大,我就往不断的往后面放,或者把最后一个给他踢掉,我并不需要挪动,因为我我是操作的最后一个,并不需要移动其他的元素了。所以说呢,在占这种数据结构中,数组也是插入和删除也是 o 一 的操作, 那它本来相对于列表,它最大的缺陷就是插入删除是 o n, 那 么现在在占这种特殊的数据结构下,它的插入删除都是 o e 了,那么就更没有理由不用占了,更没有理由不用数组了。但是数组还有另外一个限制,它的长度呢,得是固定的。 我们最开始如果不知道有多少个的时候呢,得是固定的,我们最开始如果不知道有多少个的时候呢,得是固定的。我们前面也介绍过了,用动态数组, 当空间满了的时候呢,我就进行扩容,虽然扩容的时候呢,复杂度是 o n, 但是呢,把它给均摊下来,复杂度呢还是 o e。 所以 说日常我们能够见到的各种各样的站啊,基本都是用数组实现的,这也更加说明了我们之前课程中介绍的, 在各种各样复杂的数据结构能做实现的时候,只要能用数组实现的,基本都会用数组,因为它的性能实在是比其他的高出好几个档次。来,我们来看一下站的核心操作,看看我们实现的时候要实现哪些功能, 无论底层啊,用数组还是列表的实现,站对外暴露的接口都是统一的,时间复杂度呢,都是 o 一 部署,我们叫做压站或者入站,把元素就放到站顶, pop 弹站或者叫出站,就是移除并返回站顶的元素。 还有一个呢,叫做 pick, 这个不是 p i c k 减一个的意思啊,这 pick 是 查看,看一眼的意思,就只看一眼占顶的元素,但不做删除,不把它给那个 pop 出来。 is empty 呢,是一个判空,检查站里面还有没有元素 size 呢?就是计数,看看站里面当前还有几个元素, 我们约定呢,有个指标叫 top 呢,都是指向当当前占顶的元素,当那个站为空的时候, top 就 等于负一,不能等于零啊,要等于负一。 好,我们来看一下我们用数组怎么来实现占呢?首先呢,我们还是要定义一个结构体,结构体里面有两个东西,一个是我们刚才说的 top, 就 指向占顶元素的这一个指真吧,但是呢,在用数组的实现的时候,它就不是一个指真了,它相当于一个缩影的值,告诉你现在占顶是你的数组中的第几个,还有一个就是我整个这个数组, 我数组呢定义好了, ok, 这个就是我们的站了,我们来看怎么把我们的站出使化呢?其实把站申请申请了这个 note 之后呢?有了 note 之后呢,这个是它类型的指针啊, 我们用结构体,用箭头呢,其实就是结,其实就对访问结构体里面的元素对它进行负一操作,证明这种时候站是空的。 ok, 我 们有一个空的占了,现在我们还需要把这两个方法来看一下,这因为比较简单啊, is empty, 只要是看看它的占顶的这个缩影是不是负一,就知道它是不是空了。 size 呢,其实也是返回占顶的这个它所在的缩影值呢?加一。为什么要加一啊? 因为当占里面只有一个元素的时候, top 几啊?等于零,是从零开始的,不是从一开始的。好吧,所以说呢,要是这个缩影值加一才是它的个数。 ok, 这几个简单的方法实现完了,我们看三个最重要的方法, 一个是 push, 就是 入站,我们怎么把一个元素给放到这个站对应的站里面呢? ok, 现在拿到它了,其实就可以看一下这个元素啊,比如说我们想把一个 a, 先把这个,比如说这个站现在里面有零一,还有 a 这块, 我们想想把 b 给写进去,怎么写呢?先要把 top 给指向它,再把 b 给写进去。 要说呢,当然我们最好给他判断一下是不是快该扩容了,先看一下他的 top 是 不是等于 max 减一了,如果是呢,就说明他已经到他的容量极限了,你要么就扩容,要么就报错, 如果不是的话, ok, 这个操作啊,这这一步里面做了两步的事情,首先就是把我们的 top 给指到这 这个空的元素上来,比如说呢, top 加加就是加加 top 就 先对它进行操作,是不是啊?加了之后呢,再把 s data 就是 我们的站里面的 data 呢?把这个 x 因为刚才指到空这块了嘛,就把它放进来就行了。返回处, 我们再来看一下 pop, 其实 pop 应该很简单啊,就像这一样,其实跟把插入是反过来的,我们首先得检查一下是否为空,空的就不能再往外出站了,如果空就开始给他报错,现在没没有值了,看一下特殊的地方,理论上我们只需要把 top 减一, 返回 top 当前指的内容就好了,但实际上我们看实线的时候不是这样子的,我们还多传入了一个值。大家再看啊,他也是把 top 减一了,但是呢,他把这个值呢,并没有直接做 return, 他 是把这个值赋值给这个参数了, return 了一个处, 为什么要这么做呢?再看来看 pick 啊,就是 pick, 就是 看占顶的元素是什么吗?几乎也是一模一样的操作,只不过是呢,相对于那个 pop 来说,他只是不做那个减减而已,对不对啊? 因为他不需要把他给不需要移动占顶的元素,占顶原来在哪里面还是在哪,我只是看一眼而已。而这一个呢,是真的要把占顶的占顶的左减一呢?他为什么要这么干,而不直接把这个值给 return 回来?给这个函数不就行了吗? 这是 c 语言的一些特性导致的,因为我们返回的值呢,是个布尔值,在这里面报错,因为 c 语言没有异常处理, 对吧?所以说就是返回 false 就 代表报错了,它是一个布尔值,我再不能返回它里面的值了,对不对啊?所以说,我就要求你传给我一个元素,我把我那个值呢赋值给你,你去读这一个值就知道啊占顶是谁了,而不是直接把它 return 的 pick 也是一模一样的实现啊。首先如果是空了的话,就是 false, 就是 你不是,不是让你看占顶吗?占顶是空的,如果没有空呢,我就用,我就把那个 top 上的值呢给取出来。然后呢赋值给你传给我的这一个职称, 让这个职称指向的值呢,就是占顶的值了,你自己想用的时候就去读这个 v l 就 好了,这个呢,其实是一个小的技巧,大家还是要知道啊。 好,那么我们来看一下占这种数据源这种数据结构的实际的应用。首先先看一个最简单的就是括号匹配,比如说我们编辑器里面写代码,大家日常写 c 语言的代码也是啊,那你怎么知道你写的一堆代码里面有没有少一个括号呢?或者多出一个括号来。 我们知道我们用的一些智能的 ide 里面都会给我们做提醒,他是怎么知道我的括号匹不匹配的呢?实际上用的就是占这种数据结构来实现的。比如说啊,我们先看简单的,我们把中间的字母什么的都给去掉,就是一个这样的东西, 对吧?三个就是大括号跟大括号应该是匹配的,这个看起来是匹配的啊,是吧?然 我们怎么来检测?这个我们用肉眼一看就能看出来,但如果他特别复杂的时候,得用程序怎么弄呢?我们就啊从左到右扫描这一个表达式,或者扫描这一个字母串,遇到那个不是那个括号的呢,就忽略, 如果遇到的是左括号,就是左半部分,我就把它给入站。比如说像我们这一个,遇到第一个是大括号,是不是啊?把它放进来,遇到第二个是左边的中括号,把它放进来, 遇到小括号,每次都是放到占顶,是不是啊?所以占顶就是这三个了。然后遇到右边的括号呢,就会检查占顶,就是把占顶的给泡泡出来,看看泡泡出来的 泡泡出来的这一个和右边的这个括号是否是匹配的。第一次泡泡出来的是左的小括号,遇到的是右边的小括号, ok, 匹配。第二次呢, 遇到右边的那个中括号了,我们泡泡出来的是左边的中括号,也是匹配的。再往这边走,遇到的是右边的花括号,再泡泡出来的也是左边的花号,是匹配的。说明呢,我们这一个表达式就是是对的, 这就是站的一个很简单的应用啊,我们来看一下具体的使用过过程,如果是两个的话,其实就是跟刚才的过程是一样的啊,读到左边的花括号了,那么就把它给进站,这时候站里面只有个这个的花括号,是不是啊?遇第二个呢,就是左边的中括号,遇到了把它给部署进来,这时候站里面呢就有这两个元素了。 第三个字母呢,遇到他了,遇到他的时候就需要做泡泡的 check 了, ok, 我 把站顶的元素给他踢出来,一看呢, 这个跟他是不是匹配配对的是, ok, 那 我再检查最后一个,最后一个呢是右边的,只要遇到右边的呢,就把站里面的站顶给泡泡出来看了,站顶呢,正好是这个左边的花号是匹配的, ok, 这也是对的,我们来看一下代码怎么写, 首先呢,先写一个工具函数来判断两个是否匹配,就是如果左边是小括号,右边也得是他,对吧?只有这三种情况呢才是匹配的,这是一个很简单的工具函数啊,大家自己写代码的时候也可以抽象一些工具函数,这个会让我们的主体函数呢更好读一些, ok, 接下来是我们的主体函数 check, 就是 是否是合法的括号,对吧?这个就是我们的自复串, ok, 我 们定义了一个用刚才我们的站呢,定义了一个站,并且呢把站进行抽象,其实就是把 top 设成负一, ok, 然后呢,我们放循环开始便利,我们传过来的这个自控串值得。在 c 语言中其实没有自控串的概念,所谓自控串呢,就是一个连续的 差的类型,或者是一个差的数组,是不是啊?然后呢,直到最后一个元素是空,就是那个字母串了,所以我们变例呢,就是 int i 等于零,当它不等于那个空的时候呢,不断的加加就好了,我们取出当前的差来,对吧?是不是 si 就是 当前的差。 然后呢,如果叉是左括号,或者是左边的中括号,小括号,大括号都一样,就把它扑使劲占,对吧,这种时候大家记得啊,占的时候这里面用的是什么?第二个是 v 六,第第一个可是占的时针啊,所以说得把它给艾特一下,不能直接用 s。 然后呢,如 else, 如果是那个左括号,如果是右括号的话呢,对吧?右边的这三三兄弟,我们呢,首先呢就要定义一个变量,还记得大家说的吗,我们当 pop 的 时候呢,是要有一个变量来接住这个值的, ok, 然后呢检查他是否为空,如果说呢,这种时候站他已经是空的了,就说明就说明左边的从来没进来过,我遇到右边的了,那说明你肯定就是多余的了,右边这个我就直接返回 boss 就 ok 了,你你肯定是不匹配的,是不是啊?然后如果不是空的,那我就要泡, 就泡泡当前的这个就是把,然后呢把泡泡的值,大家记得第二个也是传入的得是纸针,所以说我在这里面要写艾特他,要把他的地址传进去,就是我们定义的最上面这定义的这个变量,泡泡的值呢,就会放到这里面来,我们就去这里面去取就行了。然后呢就用刚才我们定义的工具函数, 就这个工具函数 is matching pair, 对 吧?来判断一下它们是否是 match 的, 然后呢就是把 top 给它放,这时候我们就能用它的值了,是不是啊?看看这个值和我们取出来的这个括号这两个是否是匹配的, 对吧?如果不是的话就 return false, 是 的话呢,接再接着走,这放循环就好了,直到就是走完了之后,然后走完了之后呢,我们还得检查这站是否是空的, 空的才是对的,说明左边的括号全都被弹出来了吗?如果不是空的,那说明什么呢?刚才啊,其实和这一个是对应的,和这一次判空的是对应的。如果说呢,我上来就是安排我遇到右括号的时候呢,发现站是空的了,就说明什么啊? 右括号是多余的,如果说呢,我到了最后了站还没有空,就说明肯定有左括号是多出来的,都是不匹配的场景,必须他是空才是最终都是匹配的,就是一个这种计算过程。这两个呢,实际上都是在处理一些异常情况,对吧?大家其实日常写的时候可能会不会这么写, 会遇到一些程序报错啊,知道这两个判空的处理是在判有没有,就说我遇到的确实都匹配了,但是你还有多出来的,那也不行,是不是? 好,这个就是一个完整的测试函数了,再写一个 make, 然后呢,定义了一个字母串。什么?我们在这里面可以不只写括号啊,什么 abc, 其实大家用肉眼来看的话,看起来好像是匹配的,是不是啊? 咦,这个中号花括号啊,这是个匹配的,大家可以就看看取出来的值,执行一下试试是不是匹配的。 ok, 再看一下第二个,更复杂一点,相对于只是匹配括号啊,我们来进行 表达式的求值。对于人来说啊,这个我们一看就知道怎么算,是不是啊?应该先算五乘二等于十,然后再算三加上十等于十三,是不是啊?其实我们已经做了一些选择,对于计算机来说,你直接把这一个东西,但是你交给计算机进行计算,它也是能够算的,但实际计算机是怎么算的? 如果我只是教给他这一个自复串去教给他,而不是说 int a 等于三加五乘二,那个计算机自动会算,如果说我教给他的是一个自复串,他怎么来算呢?就他怎么知道一步步的是先做五乘二,再做三加三加十呢, 其实对于计算机他他一般会做两步啊,第一步就是把这一个,我们管这个种操作符在两个操作数中间的这种表达式, 但是对于计算机呢,他喜欢后缀表达式,比如说操作符呢,在操作数的后面转成三加五乘二,就可以转成后缀呢,是这样子的,三五二乘加, 那他是怎么算的呢?看啊,计算机的话,实际上我们用站,他也是用站来进行计算的,就是对于这样的一串数字,他遇的就是对于这样的一串字母串,便利从左到右扫描,遇到数字的话呢就入站,比如说像我们这三入站, 对不对啊?五入站,二入站,所以二在最顶部了,然后遇到乘号,遇到符号呢就弹出两个数来进行计算,遇到乘号了好上站,里面把二和五给弹出来进行计算,二乘五等于十,计算结果再入站,接着再扫描, 就是又遇到操作符了,遇到操作符的时候呢,再弹出两个数来计算,把十十个三弹出来进行加法运算,等于十三, ok, 结果入站,最后占顶的元素呢,就是计算结果也等于十三,大家看明白这个过程没有, 对吧?然后我们先把这个过程呢,用代码来写出来,就是首先也是写一个工具函数,就执行预算,第一个操作符,第二个操作符,然后第三个呢是操作数,对吧?如果操作啊,第三个是操作,前两个是操作数,第三个呢是操作符, 如果是加号的话,就 return a 加 b, 如果是减号就 return a 减 b, 对 吧?这样,否则呢就 return 零,这是一个工具函数,然后我们再来看看怎么做,刚才的扫描啊,也是定义一个站,对吧?并且当然我们把那个我们的表达式是一个字母串, 对吧?其实也就是一个这种类型的纸纸针,把它给传进来, ok, 然后促使化我们的站, 接下来呢也是放循环,像刚才一样去那个便利我们传进来的这个表达式自扑串,对不对啊?取出它当前表达式的这个值,如果是空呢?就 continue, 其实就是空是什么意思?就是空格嘛,就是中间这种空格,就是不不做处理, 对吧?如果是数字 is dj, 这是 c 的 标准库里面的含函数,然后呢就把它给入站,对吧?那入站就入站就好了,那你这 c 减零是什么意思?强行把它给,这是 c 语言的引式的类型转换,强行把它给转成那个数字类型, ok, 如果说呢,你是一个是一个,那个操操作符,那么怎么办呢? ok, 我 们就要把定义两个变量, 对吧?然后呢分别把那个他给弹出来,大家要注意啊,比如说像我们这里面啊,像刚才这个,你说先弹出来的是,比如说是先弹出来的是谁呀?其实对于加法、乘法都无所谓,但对于减法和除法 是二减五还是五减二,那是直观重要的,是不是啊?大家看这个进去的顺序啊,对吧?相当,也就是说先进去的在下面,所以先弹出来的应该是先弹出来的,应该是后面的数字,对不对啊?所以我们执行两次泡泡,第一个呢要给他 b, 第二个数呢?连续弹出两个数来嘛?来给了 a, ok, 然后呢再对它们进行计算,就是 apply operator 刚才我们写的这个工具函数,对吧?把 ab 和那个操操作符传进来,就给我们结果了, 对于除法和对于减法和除法顺序是直观重要的,所以说我们弹出来的时候一定要注意,先给 b, 再给 a, 好计算出结果来,怎么办呢?再把再把算出来的结果再入站,反复的执行这个循环,直到扫描完成。那么我们最后的结果是什么呢?就是占顶元素把它泡泡出来返回,就是我们的计算结果, 非常简单。那只有一个问题了,那这个中缀表达式是怎么写出来的,对吧?你说三加五乘二, 那就是我也写不这一个,因为比较简单啊,直接就是三五二乘加,对吧?那为什么不是加乘而是乘加呢?其实这是我们操作符的优先级导致的,对吧?包括如果有括号的时候,位就更不一样,如果三和五之间有括号呢?那你这么写就是错的了,对吧? 肯定不能这么写,因为他要先算,他肯定就是三五,然后加,然后二,这乘号就在这里面了, 对吧?就顺序是变的,根据操作符的优先级或者小括号呢,都是影都都是能够影响这个优先级的,影响我们的运算顺序的。我们在脑袋里面已经完成了这个优先级的判断了,但是对于计算机来说,他怎么断的? 有说这一个中缀的表达式是怎么变成一个后缀表达式的,这是有技巧的,但这个转变的过程中呢,我们也利用了站,所以说呢,这是我们。其实呢,让计算机算出一个字母串的表达式来,它是分两步的,第一步呢,它要把这个中缀的字母串给变成后缀的, 第二步呢,再用刚才我们讲的这个算法来进行一步步的操作来计算值,那么我们来看看,我们还需要一个工具函数,就是把一个这么复杂的中,就是这现在是一个中缀的表达式,怎么把它变成后缀的呢? 实际上有是也是有一个著名的算法的,就按照这个算法啊,他也是利用他建了一个站,叫符号站来暂存运算符来调整运算顺序,他呢是有几个规则的,第一呢,就遇到空也是跳过,这个不用说,对吧?这种是不用处理的, 如果遇到数字呢,他就直接把它打出来,其实就是给你把后缀表达式呢给输出出来,如果遇到左边的小括号就入站, 如果遇到右边的小括号呢就出站,直到遇到左边的小括号为止,强行做一个匹配嘛,对吧?就是把那个如果呢还有可能是什么遇到,你看遇到数字就直接输出来, 如果遇到小括号呢就入站,遇到右边的小括号呢就不断的把站里面的东西弹出来。还有可能遇到什么运算符, 遇到运算符呢是比较复杂的,他是其实他是分了几步啊,他第一要检查当前占顶的那个操作符的是不是操作符,如果是操作符的话,优先级是不是比自己大,或者等于自己,比如乘号的优先级呢,就大于等于乘号,也大于等于加号, 对吧?如果是减号呢,他的操作符呢就小于乘号或者是除号,直到遇到直到就说他占顶的的操作符不是操作符了,或者是他的操作符呢?优先级不如自己的时候,他呢再入站, 说出来有点绕啊,我们带着大家看一个实际的例子就行了,一共就这五种,可能等于空格的时候我没写啊。好,我们来看看这个中缀表达式,他已经足够复杂了啊,我们来看看从左到右扫描遇到的第一个字母是什么?九,对应数字的话就直接输出, ok, 我 们的后缀表达式的第一个字母就是九, 然后第二个呢?中这个跳过,然后遇到加号,遇到操作看看啊,遇到计算符怎么办? 看看站顶有没有大于自己的熨刻符,有的话要弹出来,然后再入站,结果呢,他的站是空的是不是啊?那就没有了,那就加号,直接入站,站是空的就直接入站了, ok, 遇到小括号的左半部分也是入站不做输出啊,就是入站,这个越往后越表示站顶啊,在我这个写法里面, 小括号后面遇到三了,遇啊,但是这个空格也是忽略掉啊,后面我就不说空格的问题了,遇到三了,数字直接输出, ok, 那 我们的站是没有变化的,这个符号站没有变化,然后呢,我们的输出里面变成九三了,这个就是 alt put。 ok, 然后呢,遇到的是减号,又是一个运算符,那么我就要看占顶有没有大于看,直接看占顶是不是大于自己的那个 运算符没有,占顶是小括号是不是啊?那么他就直接入站就行了,这种时候输出出来的东西也不变啊, ok, 减号之后呢,我又遇到数字一了,是数字呢,就直接输出,也说这个站也是不变的,输出就变成了九三一。 好,又遇到右括号了,遇到右括号呢,我就不断的把弹出来,直到遇到左括号为止。所以呢,我要把减弹出来之后是放到输出里面的,这就变成了九三一减了。 然后呢,直到遇到小括号为止。他遇到小括号之后也要把小括号给弹出来啊,然后弹出来的东西没用,直接丢掉就好,不做输出,因为我们的后缀表达式里面是没有括号的。 括号是用来处理特殊的优先级的吗?我们的后缀表达式交给计算机的时候就没有优先级了,就是从左往右算就好。好了。 ok, 然后呢,我们再看看后面再遇到的是什么了,就是乘号了,我又要判断乘号,现在的占顶呢?现在是什么呀?占顶加是吧?加大于自己吗? 又现几不是。 ok, 那 我就直接入站了,对吧?它就变成了输出呢,也不变。再往后呢,又遇到乘以五了,是数字就直接输出,站也是不变的。然后再往后又遇到加号了, 遇到加号之后,占顶是加和乘,那么占顶的优先级大于自己吗?大于等于自己吗?是大的,所以我要把乘号呢也输出。那输出弹出乘号之后呢?它的占顶呢?就变成这个加号了,对不对啊?加号大于等于加号吗?也是的,所以我也要把这个加号输出, 那这种时候站是空了。嗯,没问题了,我就自己这个加号入站。所以说大家这块要引起重视啊,他是把这两个都给弹出来了,放到输出里面了,然后他自己又入的站,而不是只弹出了他这一个,看起来好像是,实际上他是弹了这两个自己再进去, ok, 这种时候就变成这样子了。再往后呢,遇到四了,遇到数字最简单了,直接输出, 对吧?然后呢,四后面是除号, ok, 那 我之后占顶是什么?加号,加号的优先级是小于除号的,是不是啊?那我就直接入站就好了,输出呢,也没什么变化。再往后呢,遇到数字二了,然后呢?遇到数字二之后呢,我就直接输出, ok, 这种时候呢,站里面还有两个元素,还有两个操作符。最后一步呢,我们就是清空站,所以说呢,就变成除号加号,这样我们一个后缀表达式就给算出来了,那么我们来看看,按照刚才的规则,这个计算是对的吗? 我们看看这个表达式,我们第一步算什么呀?第一步肯定是算三减一嘛,是不是因为在括号里面嘛?你看我们这个啊,再带着大家回顾回顾刚才我们的计算的过程,遇到数字入站,遇到符号弹出两个数来进行计算,那按照这个规则的话, 遇到数字入站,入站,入站,这是一个新的站了,不是刚才这个符号站了,就是到了第二步进行计算去了,好,遇到减号了,把两个数字出站,第一个给是 b, 第二个才是 a 呢,是不是啊?大家还记得这个顺序吗?先弹出来 b, 再弹出来 a, 比如说我就执行 a 减 b, 对 吧?先弹出来的是一,再弹出来的是三,那就是三减一,是不是正好啊? 对吧?这样就变成了九跟二了。然后第二步我们算什么?肯定就是二乘五嘛,大家可以看到又遇到五了,近战遇到乘号了,把这两个再弹出来,就是是不是九,这样的计算过程就是对的了,所以说呢, 知道站的妙用了吗?有了站之后呢,我们就能做这种,让计算机来做,这么复杂的这个大家用肉眼还能算出来,如果他后面再长一万倍,我们人就算不出来了,交给计算机还是轻轻松松,这就是站这种数据结构的一个妙用。 看一下代码实现过程。首先呢,我们也是写一个工具函数来处理各种操作符的优先级,就是乘号,除号的优先级,最高返回二,加号减号呢,都返回一,如果遇到小括号就返回零,这种时候我们就知道操作符的优先级了,对吧?然后呢,让我们做主函数的实现, 我们呢主函数有两个参数,第一个自辅串呢,是两个自辅串吗?第一个自辅串呢,就是我们的输入,实际上就是我们的中序表达式,对吧?第二个呢是我们的输出,最终呢我们会把结果写到它里面来, 第一个呢还是初步化一个符号占就叫 s, 第二个呢,定义一个变量,这个变量实际上就是我们这个后缀表达式的写入的位置,因为我们需要不断的往后面写, 然后对对那个我们的输入,对我们的中序表达式呢,中缀表达式进行便利,跟刚才一样,取到他当前便利的看,取到当前便利的字体,我们来看刚才也就是我们处理这几种情况, 对吧?大家看啊,对于第一种情况,如果是那个空格直接跳过,如果是数字的情况下,那么我们数字是什么呀?直接输出,是不是啊?直接输出就是往我们的结果里面,这为什么要做加加? 因为第二次的时候就要往他后面一个元素去写了吗?好,如果遇到左括号是什么呢?左括号就直接入站,这个简单,对吧?如果是左括号,扑实进去完事了。如果是右括号,如果是右括号就干什么呢? 不断的出站,直到遇到小左括号为止,并且把左括号弹出来丢掉之前不是左括号的都要放到这个表达式里面啊,看看。 好,我们就要把弹出的元素呢给放到这里面来,我们先看一眼他是不是左括号嘛,对吧?首首先呢,我们就先看一眼,如果呢是空了,如果他当前不是空, 对吧?并且呢,他的那个弹出来的呢,也不是左括号的时候,我们就干什么呢?就把它给弹出来,就这个只是先看了一眼啊,还没有进行弹的。 ok, 把它弹出来,弹出来的这个东西呢,就把它放到后缀表达式里面,是不是啊? 把它强行转成一个字母串,拼进去 g, 再进行加加,然后呢再看一眼,对吧?看一眼之后呢,干什么?不断的做这个循环吗?直到这一个不成立了,要么就是说把它给弹空了,要么呢就是遇到左括号的为止,我们干什么啊?再把左括号弹出来给丢掉就好了,不要他。 ok, 如果遇到运算符,再带着大家回忆一下,遇到运算符呢,就要先看站内的,又站当前的操作符的,站的里面站顶的元素呢,是否优先级大于等于自己,如果是的话呢,就要把它弹出来,直到遇到不是的,然后我自己再入站, ok, 这肯定也是一个, well, 首先定义一个变量,把它往里面放,只只要它不是空,对吧?我就先看一眼是什么, 如果说呢,它不是左括号啊,如果它是左括号,或者是呢,它的优先级,弹出来的这个优先级啊,比我小了,就不大于等于我了, ok, 我 就停下来就行了,否则我就干什么呀,不断的把它给弹出来,把它给拼到后缀表达式里面去, 是不是啊?最后呢,我自己再入站, ok, 一 共这就这几种情况,直到处理完成之后,我们看看最后一步是要干什么呀? 要把账给清空掉。所以说呢,我们只要是它不为空,我就不断的把它给清空,把它里面的内容呢,再给拼到后缀表达式里面,然后呢,对后缀表达式再拼一个结束符, ok, 这种时候我们就拿到后缀表达式了, 对吧?因为这个我们是倒过来讲的啊,先给大家讲的是,有了后缀表达式,我怎么算?用占来计算,然后呢,再说我们如何得到这个后缀表达式。实际我们在运行代码的时候,先肯定是要把中序调我们刚才写的函数给转成后续,然后呢,再用刚才这个算法用占, 两次都用了战,这是战的两次应用啊,算出结果来了就好,稍微有点烧脑。不过呢,可以看到用战还是能做这种反反复复的这种操作的。实际上战的应用场景呢非常多,凡是复合这种最后做的事情要先被撤回,或者先被处理, 或者是呢,先走到底,再一层层往回退,这种问题本质上呢,都特别适合用站来解决,我们讲完各种各样的数据结构之后呢,会讲一些常见的算法,大家在刷算法题的时候呢,感触就会特别深,有几类问题,比如说深度优先搜索, 先走到最深处,然后再按相反的顺序回退,层层展开并回溯。还有呢,各种各样的回溯算法,保存决策的路径,失败的时候呢,就快速撤销最近的一次选择,还有各种各样的地归操作。 我们知道啊,就是各种各样的排,就是有一些规定排序啊,快速排序等都是默认我们是用递归来实现的,很多时候呢,会考察我们非递归的实现,这种时候呢,基本上就是手写模拟站来保存函数的状态,只要遇到这三类问题,基本上都是用站可以轻松解决的。 随着我们刷算法题越来越多啊,感触会深非常多。然后呢,站出来在我们刷这种常见的算法题呢,在实际的用处呢,也是非常非常之多的。比如说呢,我们最常见的函数的嵌套调用, 实际操作系统,在调用函数的时候呢,每次遇到一个函数,就会把函数还有他的参数列表,他的返回地址呢,都压入到一个站中,让他进行操作,操作完了呢,再会返回调用他的地方, 其实就是在调用的时候,一层层的把它给押进站,调用完了,其实就最深处被调用的那个呢,反而要最先执行,是不是啊,最深处的东西呢,要最先被处理, 就是用站处理完了呢,就返回上一跳,再返回上一个函数,调用它的函数,这样的用着最深处先执行,执行完了返回, 然后处理刺身处这种我们在做飞波纳器,竖列等各种各样的时候,大家会耳闻能响,有非常多的类似操作,这种操作系统做函数调用,嵌套调用也是用的占, 然后浏览器的前进后退,我不断的呃,后退,后退,后退。然后呢又再点几个前进,它就能倒着给我们把那个我们刚才后退的内容呢就呈现出来,这是怎么做到的? 明显也是用了堆站,对吧?然后呢还有撤销操作,比如说我们在做 office 处理的时候,然后我们一二三四五做了五个步骤,后来发现呢有点问题,撤回两步,那撤回的应该是哪两步啊? 明显应该是第五和第四这两步嘛,对吧?就是你最近的这两次操作给你撤销,这明显也是站的一个典型应用, 对吧?所以我们在解决,无论是在做平常的一些,先先进去再回来就是深度探索迷宫啊,这种用低规啊,回溯这种,一看到这种基本上就要往站这个想,能让你刷算法题的速度呢快两倍。 然后呢再一种就是对于操作系统啊,对于我们常见的各种各样实际应用,站的使用呢也是非常多的, 可以把站呢就理解为是一个历史记录的管理者,只要是涉及到我还需要记住之前这块我路过过,我稍后回来再处理,基本上只要有遇到这种思路的用站就能给处理好。好,我们最后呢做一个总结,站是一种操作受限的限行表, 对吧?他的受限在哪啊?就只能对尾部进行插入和删除,可以用数组,大部分应该用数组实现,特殊情况用列表, 但正是因为这种限制呢,就让他呢在处理嵌套关系回溯问题是非常强的,常见的操作呢,都是 o 一 的对吧?基于数组的实现呢,为什么要经常用数组呢?简单高效, 虽然容量固定,但是用动态数值把它时间摊平之后呢,操作的时间复杂度也是 o 一, 基于电表呢,它是不容易溢出,然后呢,但是会有一些额外的节点的开销,所以并不是特别常见。站呢,是先进去的,要最后被处理对吧?最后进去的反而就是先要处理, 这个呢,会有点反直觉,我们只要是理解他的时候,记住最开头那一个一盘对吧?一摞盘子的例子对吧?无论你洗完盘子也是往最最顶上放,用的时候呢,也是先用最顶部的,导致最先放下去的有可能一年都没有用到过,除非家里面要请客吃一次大餐, 这样呢,会稍微有点不公平。而对于我们日常生活中啊,其实更更常见的是对列,排队可以说是无处不在的,对吧?这是一个先先到后,先来后到,是一个公平的世界。 在日常生活中都这么常见,在计算机的实际使用中,对列的事就更更加多了。在下一个视频中呢,我们就重点给大家介绍一下对列,这个也是在刷各种题目常见的一种数据结构。

兄弟们,我现在在这个截图之夜的现场,在我身后这个地方,就是刚刚昆仑平台用实力打造的一款越野车 t 三。看这展台已经被围的水泄不通了,哎,就这内饰,你们看怎么样?科不科幻?就这个尾部方面啊,看起来也是很硬朗啊,一看就是个硬派越野车型。这么的,咱先带你们看一下这个昆仑平台到底怎么样,够不够硬,能不能打啊? 刚才截图已经在展会上给大家介绍了,这是基于火星架构打造的昆仑平台,它不仅规划了插混纯电增成的多种动力组合,而且让我看到了很多非常劲爆的配置。今天一定要给你们好好洗澡洗澡。路虎卫视同款的空气悬挂和智能使量控制的四电机,这四个轮子都能 独立控制,精准控制扭距,能实现一千三百匹的马力,反正百公里加速也不多,就三秒集吧。同时啊,这个昆仑平台还实现了相当硬核的越野能力,包括最高三百六十毫米的离地间隙跟一米一的涉水深度,什么后轮转向啊,限行模式都不在话下。关 t 三,我们只能看个大概。外观方面,大家可以看得出来是一个非常硬朗的硬派越野车型,横竖看着都很霸气,这叫什么纵横百合豪横,今年这个旅游月不是爆火吗?这个车正在召唤你呢,反正谁也不好使,横竖我必须得出去玩。作为昆仑平台下的新车型, t 三可能拥有我们刚才说的所有配置。 透露出来的消息我们可以知道这辆车是一个智能使量控制的四电机,相对于普通电机来说响应更迅猛,工作效率也更高。就这玩意都是一般豪华电车才有的配置,这真能看得出来。捷途这次是下血本,轴距带宽三零五零,不知道捷途啊,这次是不是要批量出这个越野车型。非承载式的车身,独立大梁的配置,给 这个车提供了更高的稳固性和独立改装的可能。总之啊,这辆车的定位就是给户外的旅行者提供比较专业的功能和保障的车型,打破更多壁垒,探索更多未知的可能性。 对了,还有一个好消息值得大家去关注一下子。今年二月份的时候截图官方发布了旁边这台旅行者,这款车在今年下半年将正式上市。你们对这款车价格跟配置方面还有什么期待,都可以评论区下方留言啊。

哈哈哈哈哈哈 the chip of hatula mini gym of groom machine can show potashlank or solos cube blame do we are doing some large pajata lake machine canyon yonderanda show potassius land cramei bisued shimoukin pudon lamo。

哎哎哎,不要踩刹车,感觉有点感觉,感觉有点快,你也不能这样踹,你一百一直接踹到九十的话,后边会有车追尾的,用油门调整速度,不能这样踩刹车,你会导致事故发生的啊,不要一脚油门一脚刹车啊,在高速上我们多发都是用油门控制速度而已,不要搞的后边追尾啊, 实在不行再用刹车。就是说你靠近的速度太快了,杀一下上去了他踩刹车,那你跟他踩,如果他们只是调整速度,你就跟他们用油门调整速度就行了,然后我们后面没车,我让你知道松开油门怎么减速的。你现在一百零五啊,好,油门全放掉,什么都不踩,你看刚刚是一百零五,现在是差不多八十了吧。就是说 我们在高速上直接松开油门,那个速度降下来是很快的,他是很快就会降速的,现在你是面对着两个大车了,危险是你自己找的, 他们都走了,那些小轿车都走了,前面是大货车,右边是大货车,这个空间是最危险的。我始终就感觉要离他远一点。我离他远一点有两种方式啊,一种就是我慢一点让他跑,等一下整个路上你就最慢那一辆。那肯定不行啊,那你还有另外一种方法,离他远一点,就算这个货车 我们要离他远一点怎么办?我们把它超了呀,把它超了我们就离他远一点了,他也不会给我造成危险啊,对不对?所以我们要学着去超车,而不是学着我慢一点。