2013-10-26 23:19 21255人閱讀
題目:http://oj.leetcode.com/problems/generate-parentheses/算法
描述:給定一個非負整數n,生成n對括號的全部合法排列。編程
解答:數組
該問題解的個數就是卡特蘭數,可是如今不是求個數,而是要將全部合法的括號排列打印出來。post
該問題和《編程之美》的買票找零問題同樣,經過買票找零問題咱們能夠知道,針對一個長度爲2n的合法排列,第1到2n個位置都知足以下規則:左括號的個數大於等於右括號的個數。因此,咱們就能夠按照這個規則去打印括號:假設在位置k咱們還剩餘left個左括號和right個右括號,若是left>0,則咱們能夠直接打印左括號,而不違背規則。可否打印右括號,咱們還必須驗證left和right的值是否知足規則,若是left>=right,則咱們不能打印右括號,由於打印會違背合法排列的規則,不然能夠打印右括號。若是left和right均爲零,則說明咱們已經完成一個合法排列,能夠將其打印出來。經過深搜,咱們能夠很快地解決問題,針對n=2,問題的解空間以下:ui

按照這種思路,代碼以下:spa
- 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);
- }
- }
網上對該問題的解答很是多,無一例外都採用了遞歸,可是鮮見和上面思路如此清晰的算法。上述算法的思路是不少問題的通解,值得仔細研究。.net
做爲一個例子,看一下數組的入棧出棧順序問題:給定一個長度爲n的不重複數組,求全部可能的入棧出棧順序。該問題解的個數也是卡特蘭數,根據上面的思路,咱們也能夠寫出一個相似的代碼:code
- 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只能在頭部刪除元素,因此沒有采用。