题目描述

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

例如,给出 n =_ _3,生成结果为:

  1. [
  2. "((()))",
  3. "(()())",
  4. "(())()",
  5. "()(())",
  6. "()()()"
  7. ]

题解

暴力法

参考自官方

暴力法的难点在于判断暴力生成的序列组合是否有效。其实方法很简单,从左往右统计左括号和右括号的数量,只要左括号数量一直大于等于右括号数量,并最终左括号数量等于右括号数量即可。

  1. private static bool Valid(char[] current)
  2. {
  3. var balance = 0;
  4. foreach (var c in current)
  5. {
  6. if (c == '(') balance++;
  7. else balance--;
  8. if (balance < 0) return false;
  9. }
  10. return balance == 0;
  11. }

暴力法完整代码:

  1. public static IList<string> GenerateParenthesis(int n)
  2. {
  3. var combinations = new List<string>();
  4. GenerateAll(new char[2 * n], 0, combinations);
  5. return combinations;
  6. }
  7. private static void GenerateAll(char[] current, int pos, List<string> res)
  8. {
  9. if (pos == current.Length)
  10. {
  11. if (IsValid(current)) res.Add(new string(current));
  12. }
  13. else
  14. {
  15. current[pos] = '(';
  16. GenerateAll(current, pos + 1, res);
  17. current[pos] = ')';
  18. GenerateAll(current, pos + 1, res);
  19. }
  20. }
  21. private static bool IsValid(char[] current)
  22. {
  23. var balance = 0;
  24. foreach (var c in current)
  25. {
  26. if (c == '(') balance++;
  27. else balance--;
  28. if (balance < 0) return false;
  29. }
  30. return balance == 0;
  31. }

回溯法

大体思路和暴力法类似,只是仅在序列依然有效时才添加 ‘(‘ 或 ‘)’。

  • 只要左括号数量小于 n,就能拼接一个左括号

  • 只要右括号数量小于左括号数量,就能拼接一个右括号

  1. public static IList<string> GenerateParenthesis(int n)
  2. {
  3. var res = new List<string>();
  4. Backtrack(res, "", 0, 0, n);
  5. return res;
  6. }
  7. private static void Backtrack(ICollection<string> res, string current, int open, int close, int max)
  8. {
  9. if (current.Length == 2 * max)
  10. {
  11. res.Add(current);
  12. return;
  13. }
  14. if (open < max) Backtrack(res, current + "(", open + 1, close, max);
  15. if (close < open) Backtrack(res, current + ")", open, close + 1, max);
  16. }