时隔将近三年,我又准备在简书上发文章了,初衷依然是将自己对一些算法的理解和感悟记录下来。今天开始动笔时看了一下自己当年发的第一篇文章,看了好久才看懂,我真是醉了。明明就是一个很简单的入栈出栈序列问题,非要讲得那么复杂,扯什么卡特兰数,都tm写的啥玩意儿。我觉得知识的感悟和理解切忌搞一堆看似高大上的概念,重要的是要知道这些概念背后的逻辑是什么,提出这些概念的人当时他们是怎么想出来的。所以我打算用自己的语言,言简意赅地把第一篇文章中的问题再梳理一遍。
问题描述
对一个栈进行次入栈和次出栈操作,问满足条件的入栈出栈序列一共有多少个?
问题分析
我先摊牌,文章一中的纸杯问题问题本质上就是这样一个题意非常简单的问题,我们先解这个简单问题,回过头来再仔细分析问题之间是如何转化的。
首先对于入栈出栈问题,毋庸置疑的是第一个操作一定是第一个元素入栈。考察出栈时的情况,的出栈将这个元素分成了两个部分,一部分是在此之前进栈的元素(此时也已经全部出栈),另一部分是尚未进栈的元素。根据栈后入先出的特性,出栈时栈一定是空的,所以对于尚未进栈的那个元素,完全就是一个子问题,而对于已经进栈的元素,仅仅是在头尾分别加上了的入栈和出栈,本质上也是一个子问题,于是我们就得到了下面的公式:
其中表示刚进栈就立即出栈的情况,表示最后出栈的情况,怎么样,是不是一下子就豁然开朗。理解到这一步对于解决这个问题就已经足够了,具体的代码实现我就不写了,可以用递归也可以用动态规划。
问题转化
下面我就来具体讲一讲文章一中的纸杯问题是怎么转化成出栈入栈问题的。文章一传送门:不缠绕的纸杯电话,只看一下题目描述即可。问题是如何转化的这一点非常重要,因为许多问题本质上都是一类问题,只是披上了不同的外套。对于纸杯问题,我们首先抽取题意:在一个圆周上有个点,将它们两两相连且保证连线不相交,问共有多少种连线方式。首先我们在这个点中任选一个点作为起始点,因为这些点都是等价的,所以选谁都一样。为了便于分析,我们选择最上面的点,用这个点模拟栈中第一个元素的入栈。在剩下的个点中,我们选择一个点与相连,注意这条连线将整个圆分成了两个部分,左边和右边,因为连线不能相交,它们只能在各自的部分中两两相连。怎么样,是不是有一种似曾相识的感觉,我们又将这个问题分解成了两个子问题。为保证能够两两相连,两个部分的点的个数都必须是偶数,所以只能选择与、...这些下标为奇数的个点相连(假定下标是以逆时针方向增长的)。
那么怎么模拟其实就显而易见了,我们将连线中下标较小的点视为入栈,大点视为出栈,以为例,就表示入栈后立即出栈,表示最后出栈。到了这一步是不是就发现,这个问题本质上就是入栈出栈序列问题,列出来的公式都是一模一样的。
扩展
看了上面的解答,下面这个问题你能解决吗:
个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?
码字不易,点个赞再走吧
网友评论