diff --git a/thu_dsa/chp4/notice.md b/thu_dsa/chp4/notice.md index 43ef0c5..b8e570b 100644 --- a/thu_dsa/chp4/notice.md +++ b/thu_dsa/chp4/notice.md @@ -9,7 +9,7 @@ 对于阶乘运算符,关键在于这是一个单目运算符,并且具有最高的优先级。由于它是一个单目运算符,因此在计算时只需要对操作数栈进行一次弹栈;而由于它具有最高的优先级,因此如果它在操作符栈中,则必然存在于栈顶,并且只能有一个阶乘运算符在栈中。需要注意的是,阶乘运算符的优先级与左括号是不可比较的,因为阶乘之后不可以跟一个左括号。 -指数运算符的优先级仅次于阶乘,它的特殊性在于在通常约定的语意下,指数运算符是右结合的(其他诸如加法、乘法的运算符时左结合)。这就意味着`x^y^z`需要被理解为`x^(y^z)`而非`(x^y)^z`。为此,只需要对`prio`数组的这一项进行修改,使得`prio[POW][POW] = '<'`即可。 +指数运算符的优先级仅次于阶乘,它的特殊性在于在通常约定的语意下,指数运算符是右结合的(其他诸如加法、乘法运算符是左结合)。这就意味着`x^y^z`需要被理解为`x^(y^z)`而非`(x^y)^z`。为此,只需要对`prio`数组的这一项进行修改,使得`prio[POW][POW] = '<'`即可。 > 操作符栈的大小 @@ -36,15 +36,17 @@ bottom top + 在进行实质的计算时,操作数栈不得为空。 + 输入表达式是否符合中缀表达式的规范。 -对于第三个方面,检验的方法是,在任意操作符即将入栈时(即操作符之间的优先级比较以及可能的弹栈与计算操作已经结束),操作数栈的规模恰好比操作符栈大1。需要注意,上述结论中,不应该计入`\0`以及`'(`。 +对于第三个方面,检验的方法是,在任意操作符即将入栈时(即操作符之间的优先级比较以及可能的弹栈与计算操作已经结束),操作数栈的规模应该恰好比操作符栈大1。需要注意,上述结论中,不应该计入`\0`以及`(`。 对这个结论可以利用数学归纳法证明,主要就是对单目运算符和双目运算符进行讨论,这里不再赘述。 ## 卡特兰数 -括号匹配问题,栈混洗问题,异构二叉树的数量问题,都可以归入到卡特兰数,这里做一个简单的说明。 +括号匹配问题,栈混洗问题以及异构二叉树的数量问题,都可以归入到卡特兰数的范畴,这里做一个简单的说明。 -异构二叉树的数量应该是这里最明确的问题,设`SP(n)`表示节点数量为`n`的二叉树的异构数量,以其中一个节点为为根,设左子树的规模为`k`,右子树的规模则为`n - k - 1`,因此有 +> 异构二叉树的数量 + +异构二叉树的数量应该是这里最明确的问题,设`SP(n)`表示节点数量为`n`的二叉树的异构数量,以其中一个节点为为根,设左子树的规模为`k`,则右子树的规模为`n - k - 1`,因此有 $$ SP(n) = \Sigma_{k = 0}^{n - 1}SP(k)SP(n - k - 1) @@ -52,7 +54,9 @@ $$ 其中,定义`SP(0) = 1`,这就是卡特兰数的递推公式。 -对于括号匹配问题,设这里有`n`对括号,$S_n$为一个匹配的序列。我之前产生过一些错误的看法,比如可以把$S_n$分解为两个各自匹配的序列$S_k$和`S_{n - k}`,即 +> 括号匹配问题 + +对于括号匹配问题,设这里有`n`对括号,$S_n$为一个匹配的序列。我之前产生过一些错误的看法,比如可以把$S_n$分解为两个各自匹配的序列$S_k$和$S_{n - k}$,即 $$ S_n = S_k S_{n - k} @@ -64,7 +68,7 @@ $$ SP(n) = \Sigma_{k = 0}SP(k)SP(n - k) $$ -答案当然是错误的,因为这里会产生重复计数的问题。比如设$S_3 = ()()()$,$S_1 = ()$,$S_2 = ()()$,那么$S_3 = S_1S_2 = S_2S_1$,会被计数两次。另一种划分$S_n = S_k()S_{n - k - 1)$也是同样的问题。 +答案当然是错误的,因为这里会产生重复计数的问题。比如设$S_3 = ()()()$,$S_1 = ()$,$S_2 = ()()$,那么$S_3 = S_1S_2 = S_2S_1$,会被计数两次。另一种划分$S_n = S_k()S_{n - k - 1}$也是同样的问题。 因此,这里正确的划分应该是$S_n = (S_k)S_{n - k}$,容易证明,此时没有上面的重复计数情况。此时就可以得到 @@ -72,12 +76,14 @@ $$ SP(n) = \Sigma_{k = 0}^{n - 1}SP(k)SP(n - k - 1) $$ -该结论和二叉树的问题一致。实际上,考虑二叉树的一次中序遍历,如果把每个节点的入栈视作一个左括号,出栈视作右括号,此时两个问题是完全等效的。此时$S_n = (S_k)S_{n - k}$中包围$S_K$的括号,就对应了二叉树的根节点。 +该结论和二叉树的问题一致。实际上,考虑二叉树的一次中序遍历,如果把每个节点的入栈视作一个左括号,出栈视作右括号,此时两个问题是完全等效的。此时$S_n = (S_k)S_{n - k}$中包围$S_K$的括号,就对应了二叉树中根节点的入栈与出栈操作。 + +> 栈混洗 对于栈混洗问题也可以得到相似的结论。考虑`n`个元素的栈混洗,设首元素在第`k`个位置出栈。显然,首元素出栈后栈为空。此时不难得到 -``` +$$ SP(n) = \Sigma_{k = 0}^{n - 1}SP(k)SP(n - k - 1) -``` +$$ 实际上,把栈混洗的入栈视作一个左括号,出栈视作一个右括号,则栈混洗问题与括号匹配问题完全一致,与二叉树异构也是完全一致的。此时,首元素的入栈和出栈分别对应了根节点的入栈和出栈。