Leetcode 22 括号生成的五种解法 C++

题目:

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。

示例:

输入:n=3

输出:

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

 

解法一:暴力二叉树法

思路

  1. 把所有的组合情况用二叉树表示出来 ,每一个不为空的节点代表一个前括号或者后括号。前括号标记为1,后括号标记为-1。
  2. 用改良版中序遍历找到合理的叶子节点。在此过程中只要控制两点:
  • 路径上标记和大于等于0,即后括号数不能比前括号数多
  • 叶节点的路径标记和为0,即前括号与后括号数目相等
class Node {
public:
    int num;
    Node* right;
    Node* left;
    Node(int i) :num(i) {
        right = left = nullptr;
    }
};
class Tree {
public:
    Node* head;
    vector str;
    Tree() {
       head = new Node(1);
    }
    void add(Node* tem) {
        if (tem->left!= nullptr&&tem->right!=nullptr) {//如果tem的孩子不空
            add(tem->left);
            add(tem->right);
        }
        else{
            tem->left = new Node(1);
            tem->right = new Node(-1);
        }
    }
    void mida(Node* n,string str1,int sum) {//前提n不为空
        if (n->num == -1) {
            str1 += ')';
            sum += -1;
        }
        else if (n->num == 1) {
            str1 += '(';
            sum += 1;
        }
        if (n->left != nullptr&&n->right!=nullptr) {//n不是叶节点
            if (sum < 0) {//如果出现了不合理括号
                return;
            }
            else {
                mida(n->left, str1,sum);
                mida(n->right, str1,sum);
            }
        }
        else {//n是叶子节点
            if (sum == 0) {//前括号与后括号数目相等
                str.push_back(str1);
            }
        }
    }
};
class Solution {
public:
    vector generateParenthesis(int n) {
        Tree a;
        for (int i = 0; i < 2*n-1; i++) {
            a.add(a.head);
        }
        a.mida(a.head, "", 0);
        return a.str;
    }
};

这是我第一遍做的方法,优点:思路清晰,简单易懂;

缺点:耗时太长,内存消耗大

还是来学习大神们的方法吧:

 

解法二:直接递归法

思路:朴实无华的递归。

(哭了,为啥我就想不到这样做呢QAQ)

class Solution {
public:
    vector generateParenthesis(int n) {
        vector res;
        func(res, "", 0, 0, n);
        return res;
    }

    void func(vector& res, string str, int l, int r, int n) {
        if (l > n || r > n || r > l) 
            return;
        if (l == n && r == n) { 
            res.push_back(str); 
            return; 
        }
        func(res, str + '(', l + 1, r, n);
        func(res, str + ')', l, r + 1, n);
        return;
    }
};

耗时和内存也十分优秀:

Leetcode 22 括号生成的五种解法 C++_第1张图片

 

解法三:套括号法

思路:运用二维vector,由0对括号和1对括号推出2对括号的组成,再由0对、1对、2对括号推出3对括号的组成……以此类推。

这个vector dp的结构是这样的:

dp[0] 

" "
dp[1] " () " 
dp[2] " (())" , "()()"
dp[3] "((()))" , "(()())" , "()(())" , "()()()" , "(())()"
class Solution {
public:
	vector generateParenthesis(int n) {
		if (n == 0) return {};
		if (n == 1) return { "()" };
		vector> dp(n + 1);
		dp[0] = { "" };
		dp[1] = { "()" };
		for (int i = 2; i <= n; i++) {
			for (int j = 0; j < i; j++) {
				for (string p : dp[j])
					for (string q : dp[i - j - 1]) {
						string str = "(" + p + ")" + q;
						dp[i].push_back(str);
					}
			}
		}
		return dp[n];
	}
};

 

解法四:往返的栈方法

(这栈真是该死的甜美,叹服)

思路:将每一步的加括号结果都弄进栈,同时标记原来的括号序列。得到一个结果后,将该结果出栈,回到初始的序列。

举个栗子:

n=2,此时的栈top为"()",status=0:

  • 不能加")",status=1
  • 还差一个"(", 于是进栈 "()(" ,原来的序列status=2,新进栈的序列status=0
  • 栈top为"()(",加")" ,进栈"()()",原来的序列status=1,新进栈的序列status=0
  • 栈top为"()()",满足要求,出栈
  • 栈top为"()(",status=1,不能加"(",status=2
  • 栈top为"()(",status=2,出栈
  • ……
class Solution {
public:
    struct Data {
        string str;
        int lc;
        int rc;
        int status;
    };
    stack st;
    vector generateParenthesis(int n) {
        n *= 2;
        vector res;
        st.push(Data{ "", 0, 0, 0 });
        while (st.empty() == false) {
            Data& t = st.top();
            if (t.str.size() == n) {
                res.push_back(t.str);
                st.pop();
                continue;
            }
            if (t.status == 2) {
                st.pop();
                continue;
            }
            if (t.status == 0) {
                if (t.rc < t.lc) {
                    st.push(Data{ t.str + ")", t.lc, t.rc + 1, 0 });
                }
                t.status = 1;
                continue;
            }
            if (t.status == 1) {
                if (t.lc  - t.rc < n - t.str.size()) {
                    st.push(Data{ t.str + "(", t.lc + 1, t.rc, 0 });
                }
                t.status = 2;
                continue;
            }
        }
        return res;
    }
};

 

参考了以上代码后,感到无比快乐的我又写了列表法

方法五:暴力列表法

思路:与方法一类似,list str用来记录括号序列,list sign用来记录标记值的和。

class Solution {
public:
    vector generateParenthesis(int n) {
        n = n * 2;
        vector a;
        list str;
        list sign;
        str.push_back("(");
        sign.push_back(1);
        while (str.front().length() < n) {
            if (sign.front() >= 0) {
                string str1 = str.front() + "(";
                str.push_back(str1);
                sign.push_back(sign.front() + 1);
            }
            if (sign.front() > 0) {
                string str2 = str.front() + ")";
                str.push_back(str2);
                sign.push_back(sign.front() - 1);
            }
            str.pop_front();
            sign.pop_front();
        }
        while (str.empty() == false) {
            if (sign.front() == 0) {
                a.push_back(str.front());
            }
            sign.pop_front();
            str.pop_front();
        }
        return a;
    }
};

内存消耗有了很大进步,但是运行时间依旧不怎么样。

希望能早日熟练运用大神解法。

注:题目来自leetcode,部分代码来自评论区,感谢各位的分享。

你可能感兴趣的:(leetcode)