1. 程式人生 > >Catalan數,括號序列和棧

Catalan數,括號序列和棧

markdown 證明 支持 bbbb cnblogs 例如 另一個 演示 循環

全是入門的一些東西.基本全是從別處抄的.

: 支持單端插入刪除的線性容器. 也就是說,僅允許在其一端加入一個新元素或刪除一個元素. 允許操作的一端也叫棧頂,不允許操作的一端也叫棧底.
數個箱子相疊就可以認為是一個棧,只能在最頂端加入一個新箱子或拿走一個箱子.

棧中的元素遵循後進先出(last in first out,LILO)的規律.即:更早出棧的元素,應為更早入棧者.

這是一個演示:
奇數行為棧中元素(右端可以進行插入刪除),元素以逗號隔開, EMPTY表示棧為空
偶數行為進行的操作

EMPTY
插入10
10
插入20
10,20
插入51
10,20,51
插入10
10,20,51,10
刪除一個元素
10
,20,51 刪除一個元素 10,20 插入30 10,20,30 刪除一個元素 10,20 刪除一個元素 10 刪除一個元素 EMPTY

棧混洗問題
給出三個棧S1,S2,S3,一開始S1中含有1到n的n個數字且從棧頂到棧底數字依次為1,2,3,....n-1,n.
只有兩種允許的操作:
A S1非空時從S1取出一個元素放入S2,
B S2非空時可以從S2取出一個元素放入S3.
最後S3中自底向上形成的序列稱作一個棧混洗.
例如,如果S1中一開始有1,2,3,4四個元素,那麽先進行4次A操作再進行4次B操作,將得到序列4,3,2,1.如果A操作和B操作交替進行,將得到序列1,2,3,4.
顯然,棧混洗的結果並不唯一.
一個長為n的序列的棧混洗可以表示成n次A操作和n次B操作組成的一個操作序列. 而n次A操作和n次B操作組成的一個操作序列也可以表示唯一的一個棧混洗序列.
不同的操作序列必然得到不同的棧混洗,不同的棧混洗也必然對應不同的操作序列.
並不是所有含有n個A,n個B的序列都是合理的操作序列.例如BBBBAAAA,將導致我們嘗試拿出空棧S2中的元素.
一個序列合理的條件是:對於任意m(1<=m<=2n),該序列的前m個操作中,B操作的數目不超過A操作的數目.只要滿足這個條件就能保證任意時刻不會拿出空棧中的元素.
如何判斷一個棧混洗序列是否是可能出現的棧混洗序列


例如,對於序列1,2,3,通過棧混洗可以得到[1,2,3],[3,2,1],[1,3,2],[2,1,3],[2,3,1],但是無法得到[3,1,2].如果使3最先出棧,那麽就必須先令1,2入棧,從而2會在1之前出棧,只能得到[3,2,1]
任意給出一個n和一個排列,如何判斷這個排列能否通過棧混洗得到?例如,n=5,序列為5,4,1,3,2,是否可能?
直接的思路是,直接根據我們要得到的序列,嘗試進行A操作和B操作.例如,n=5時,要使序列的第一個元素為5,就必須一直進行A操作直到5出現在S2的棧頂.之後需要4,4恰好在S2的棧頂,於是彈出4.接下來需要的1不在棧頂,因此這個序列無法通過棧混洗得到.時間復雜度顯然為O(n).其正確性也是顯然的.
棧混洗與括號序列

首先只考慮由一種括號組成的括號序列.
()()()(),(()())(),((()))都是能夠匹配的括號序列.)(,))(,())(都是不能夠匹配的括號序列.
只要將A操作與左括號"("對應,B操作與右括號")"對應,棧混洗的合法操作序列就可以和能夠匹配的括號序列一一對應.
例如,AABB對應(()),ABAB對應()()
按照我們之前的理解,如果保證序列中左右括號數目相同,那麽我們只需要掃描一遍序列並維護一個計數器,初始為0,遇到左括號+1,遇到右括號-1,只要這個計數器的數值始終非負,就說明任意一個前綴中左括號的數目多於右括號的數目,等價於這個序列是能夠匹配的括號序列.
另一種判斷括號序列是否合法的方法:初始化一個空棧S,從左向右掃描序列,遇到一個左括號將其入棧,遇到一個右括號時判斷棧中是否有一個左括號,如果有,那麽這個左括號與當前遇到的右括號相匹配.如果棧為空,那麽這個括號序列並不合法.通過這種方式,我們除了得知括號序列是否合法,還可以得知每個右括號具體是和哪個左括號匹配,還可以處理序列中出現了多種括號且只有對應種類的括號能匹配的情況.可以自行嘗試,"維護一個計數器"的方式並不能方便地擴展到多種括號的情況.
拓展:允許循環移位的合法括號序列
允許將括號序列的最左側元素拿到最右邊,問能否通過若幹次這樣的操作使得括號序列合法.
首先這樣的序列仍然需要滿足左括號和右括號數目相同. 但是除此之外還需要滿足什麽條件呢?
令人驚訝的是,除此之外並不需要滿足什麽條件.只要左括號和右括號數目相同,就可以保證能夠通過若幹次循環移位使得括號序列合法.
給出一個構造方式:將左括號視為1,右括號視為-1,求取該序列的前綴和.如果前綴和中的最小值為0那麽序列本身就是合法的.否則我們找到前綴和的最小值的出現位置(多個數值相同時取最左側的),將這個位置之前的一段序列移位到右端,得到的即為合法的括號序列.構造方式的正確性不難證明.
括號序列計數
有多少個不同的含有n對括號的合法括號序列?這個問題等價於通過棧混洗可以得到多少個不同序列.也等價於,有多少個含有n個0,n個1的長為2n的字符串使得任意一個前綴中1的數目不少於0的數目.
假設答案為f[n].邊界條件為f[0]=1,表示沒有括號/沒有元素時也算有一種方案(空串也是一種合法序列).
插播兩條高中數學:
分類加法計數原理:做一件事有m類方法,每類方法分別有A1,A2...Am種做法,那麽做這件事共有\(A1+A2+...+Am\)種方法
分步乘法計數原理:做一件事有m個步驟,每個步驟分別有A1,A2...Am種做法,那麽做這件事共有\(A1*A2*...*Am\)種方法
好了,接下來我們來將所學的用於實踐.
考慮最左側的左括號匹配的右括號在什麽位置.假設這個左括號和匹配的右括號之間有2i個括號(這個數目必須是偶數),這2i個括號排列成合法括號序列的方案數為f[i].匹配的右括號右邊還有2n-2i-2個括號,將它們排列為合法括號序列的方案數為f[n-i-1],這兩部分可以認為是兩個步驟,是獨立的,那麽總的方案數是f[i]*f[n-i-1].
對於不同的i,我們可以認為是做一件事的不同種類的方法.
於是\(f[1]=f[0]*f[0],f[2]=f[1]*f[0]+f[0]*f[1],f[3]=f[0]*f[2]+f[1]*f[1]+f[2]*f[0]\),這是一個O(n^2)的計算方式.
這裏我們得到的f[n]也叫Catalan數(卡特蘭數),它還具有很多實際意義.
紫書330~331頁給出了Catalan數的另一個實際意義:多邊形三角剖分數目,並通過另一個O(n^2)的遞推公式,推倒了O(n)的遞推公式.
另一個常用的計算公式是f[n]=C(2n,n)/(n+1)=C(2n,n) - C(2n,n-1),C(2n,n)為組合數.

Catalan數,括號序列和棧