卡特蘭數相關
(7)圓桌握手問題: 圓桌周圍有 2n個人,他們兩兩握手,但沒有交叉的方案數。
(8)本文的“n對括號正確匹配組成的字串數”問題。
雖然同屬於卡特蘭數問題,但是用同樣的方法解釋似乎行不通(看百度百科上的解釋:關於出棧的問題。還是比較容易理解的,但是我卻不能通過它理解本題)
最後發現是《程式設計之美2》這本書上的原題。其解釋如下:
分析
“卡特蘭數”除了可以使用公式計算,也可以採用“分級排列法”來求解。以 n對括弧的合法匹配為例,對於一個序列 (()而言,有兩個左括弧,和一個右括弧,可以看成“抵消了一對括弧,還剩下一個左括弧等待抵消”,那麼說明還可以在末尾增加一個右括弧,或者一個左括弧,沒有左括弧剩餘的時候,不能新增右括弧。
由此,問題可以理解為,總共 2n個括弧,求 1~2n級的情況,第 i 級儲存所有剩餘 i 個左括號的排列方案數。 1~8級的計算過程如下表:
(這裡,我搞了很久才折騰明白,行1-8表示級數–括弧個數,列0-8表示不完整匹配左括號個數。紅色為博主解釋,其他為原文)
比如:1級: 只有一個,只能是左括號(。右括號不能先出現啊。
不完整左括號個數為 1個。 (
2級:由1級的左括號(,如果給它右括號,則完整匹配;此時不完整匹配左括號個數為0,因此在0這一列下為1;
如果給再給它左括號,則不完整匹配左括號個數為2,只有一種情況: (( 個數為1
不完整匹配左括號個數為1這種情況不存在!!!因此不填。
3級:一定是不完整的,所以不完整匹配左括號個數為0 的下方 空缺;
由2級可知,原來有完整匹配 1個”()”, 現在又增加了1個括弧,那麼只能又出現了一個不完整匹配左括號
原來有不完整匹配左括號2個“((“,現在如果增加一個右括號,則不完整匹配左括號個數為1的情況+1
如果增加一個左括號,則不完整匹配左括號個數為3的情況+1
現在明白為什麼下一級的個數為上一級相鄰兩邊相加。比如3級 1列(0列起始)下=2級0列+2級2列。
依次類推:
4級:原來3級中有2個不完整1次括弧,那麼4級中就有 完整匹配 2次。
4級中不完整2次括弧=3級的不完整1次+3級的不完整3次。
4級的不完整4次括弧 只能是”((((“全左括號,所以每行最後一個數字為1,表示全左括號。
over
計算過程解釋如下: 1級:只能放 1個“(”; 2級:可以在一級末尾增加一個“)”或者一個“ (”
以後每級計算時,如果遇到剩餘 n>0個“(”的方案,可以在末尾增加一個“ (”或者“ )”進入下一級;遇到剩餘 n=0個“(”的方案,可以在末尾增加一個“ (”進入下一級。
奇數級只能包含剩餘奇數個“(”的排列方案
偶數級只能包含剩餘偶數個“(”的排列方案
從表中可以看出,灰色部分可以不用計算。
解法
關鍵程式碼為:
double Catalan(int n) { if (n == 0) return 1; for (int i = 2; i <= 2 * n; i++) { var m = i <= n ? i : 2 * n + 1 - i; for (int j = (i - 1) & 1; j <= m; j += 2) { if (j > 0) arr[j - 1] += arr[j]; if (j < n) arr[j + 1] += arr[j]; arr[j] = 0; } } return arr[0]; }
其中:
n為 Cn中的 n;
arr = new double[n + 1];//arr[i]代表有 k個括弧的時候,剩餘 “(“個數為 i的排列方案個數 arr[1] = 1;
討論
演算法複雜度為 = O(n^2),空間複雜度為 O(n+1)。相對於利用公式計算而言,此方法的優勢在於——沒有乘除法,只有加法。