22. Generate Parentheses--求n对括号组成可以组成的全部有效括号序列

描述:

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

[
 "((()))",
 "(()())",
 "(())()",
 "()(())",
 "()()()"
]

我的老实人+不择手段算法:

let cache = {};
var generateParenthesis = function(n) {
   暴力枚举
      if(n==1){
        return ['()'];
    }
    if(cache[n])
        return cache[n];
    let start = '',result = [];
    for(let i = 0; i < n; i++){
        start +='()';
    }
    result.push(start);
    for(let i = 1,l=start.length; i < n;i++){
        let move = generateParenthesis(start.slice(0,i*2).length /2),
            holdOn = generateParenthesis(start.slice(i*2).length/2);
            
        move.forEach((item)=>{
                holdOn.forEach((holdOn) => {
                    for( let j = 0,subLength = holdOn.length; j < subLength; j += 2){
                        result.push( holdOn.slice(0,j+1) + item + holdOn.slice(j+1) )
                    }
                })
            
        })
            
    }
    for(let i = 0,l=result.length; i < l; i++){
        if(result.indexOf(result[i])!==result.lastIndexOf(result[i])){
            result.splice(i,1);
            i--;
        }
    }
    cache[n] = result;
    return result;
 }

思路:给定n对括号,可以先构造一个模板,然后在这个模板中,通过移动左边1,2,...,n-1 个括号到右边的括号里面,这样就可以构造出所有可能的有效序列了,

比分说: n = 3, 那么我们可以先构造出一个模板 ()()(),而后通过移动左边的1,2个括号到右边的2,1个括号构成的序列的括号里边的位置,那么就可以构造出其他有效序列了,而这里边必须注意的是移动左边的括号往右边的括号里边放的时候,左右两个子序列也会有各自的排序方法,所以需要递归的先求出左右序列的所有排列可能,而后再进行插入操作。

事情还没完,这样左右两边都列出可能的排列之后,肯定会发生重复,所以最后我又进行了一个数组去重,真是不可原谅,最后提交的时候抛出了308ms惊人的成绩。这还是在用了cache缓存一定子序列的情况下跑的。

然后要着重贴一下官方的解答下面的一个递归算法,

var generateParenthesis = function(n) {
     /* 保持 左右平衡 */
    let result = [],
        
        produce = (curStr,left,right,curNums) => {
            if( (left == curNums )&& (right == curNums) ){
                result.push(curStr);
                return;
            }
            
            if( left < curNums){
                produce(curStr+'(',left +1 ,right,curNums);
            }
            if( right < left){
                produce(curStr+')',left,right+1,curNums);
            }
        };
        
    produce('',0,0,n);
    
    return result;
};

这个算法其实更暴力,妙在使用递归和保持左右括号平衡的方法将一个看似复杂的问题简化了,我们走一遍流程哈,假如 n = 3对,刚开始 肯定是左半边括号加,

然后进入递归

此时left为1,继续

此时left为2,继续

此时left为3了,右半括号开始加,

此时right为1,继续

此时right为2,继续递归

此时right为3,

result里边push进((()))

最后一层函数返回

倒数第二层返回,直到返回到left为2时的状态

进入到 第三个if里边,这时候 curStr = ((),继续递归

......

直到结束。

我是真的觉得这个算法很妙,确实很钦佩第一个想出此种算法的人,妙极!因为递归,所以函数上下文可以保留函数的环境,也就是保存了‘(’和‘)’的各种排列,并且是平衡的,只要平衡(即左右括号数列相等)就是有效的。

高手高手!

posted @ 2018-09-23 17:55  子龙_子龙  阅读(9)  评论(0编辑  收藏  举报