1. 程式人生 > >[LintCode] 427_Generate_Parentheses.cpp

[LintCode] 427_Generate_Parentheses.cpp

article 基本 個數 clas 而是 似的 ref 很快 push

題目:Generate Parentheses

描述:給定一個非負整數n,生成n對括號的所有合法排列。[轉載]

解答:

該問題解的個數就是卡特蘭數,但是現在不是求個數,而是要將所有合法的括號排列打印出來。

該問題和《編程之美》的買票找零問題一樣,通過買票找零問題我們可以知道,針對一個長度為2n的合法排列,第1到2n個位置都滿足如下規則:左括號的個數大於等於右括號的個數。所以,我們就可以按照這個規則去打印括號:假設在位置k我們還剩余left個左括號和right個右括號,如果left>0,則我們可以直接打印左括號,而不違背規則。能否打印右括號,我們還必須驗證left和right的值是否滿足規則,如果left>=right,則我們不能打印右括號,因為打印會違背合法排列的規則,否則可以打印右括號。如果left和right均為零,則說明我們已經完成一個合法排列,可以將其打印出來。通過深搜,我們可以很快地解決問題,針對n=2,問題的解空間如下:

技術分享
按照這種思路,代碼如下:

void generate(int leftNum,int rightNum,string s,vector<string> &result)  
    {  
        if(leftNum==0&&rightNum==0)  
        {  
            result.push_back(s);  
        }  
        if(leftNum>0)  
        {  
            generate(leftNum-1,rightNum,s+(,result);  
        }  
        
if(rightNum>0&&leftNum<rightNum) { generate(leftNum,rightNum-1,s+),result); } }

網上對該問題的解答非常多,無一例外都采用了遞歸,但是鮮見和上面思路如此清晰的算法。上述算法的思路是很多問題的通解,值得仔細研究。

作為一個例子,看一下數組的入棧出棧順序問題:給定一個長度為n的不重復數組,入棧順序一定,求所有可能的出棧順序。該問題解的個數也是卡特蘭數,根據上面的思路,我們也可以寫出一個類似的代碼:

void
inoutstack(int in,int out,deque<int> &q,stack<int> &s,deque<int> seq,vector<deque<int>> &result) { if(!in&&!out) { result.push_back(q); return; } if(in>0) { s.push(seq.front()); seq.pop_front(); inoutstack(in-1,out,q,s,seq,result); seq.push_front(s.top()); s.pop(); } if(out>0&&in<out) { q.push_back(s.top()); s.pop(); inoutstack(in,out-1,q,s,seq,result); s.push(q.back()); q.pop_back(); } }

上述代碼由於采用了棧和隊列模仿整個過程,所以顯得略微復雜,但是代碼的基本結構還是符合一個類似的基本規則:在某一個特定時刻,入棧的次數大於或者等於出棧的次數。在生成括號的問題中,我們利用一個string來保存結果,由於打印左括號時不影響打印右括號,所以無需復雜的狀態恢復。在入棧出棧順序問題中,由於兩次遞歸調用共享同一個棧和隊列,所以我們需要在返回時手動恢復其內容到調用前的狀態。在恢復時,隊列會從頭部刪除和添加,所以我們采用了deque,它可以在頭部添加和刪除元素。queue只能在頭部刪除元素,所以沒有采用。

[LintCode] 427_Generate_Parentheses.cpp