题目要求通过输入的一个数字n来确定一共有多少种合法的括号组合(所有的前后括号都能一一对应),比如n=3,那么返回的组合应该是:["((()))","(()())","(())()","()(())","()()()"];
题目中给的标签提示是动态规划和回溯,那么既然看到了动态规划我们的做法通常是比较固定的几步,如果不知道什么是动态规划可以自己去了解一下。
那么针对这道题应该是什么思路呢?
深度优先遍历(DFS)
DFS是什么:从一个顶点出发沿着一条路一直走,知道装到了南墙就回头去上一个节点选择另一条路继续走去撞南墙,直到遍历完所有的节点。
这里我们如果使用DFS作为解法的话需要考虑的就是回溯+剪枝;建立一颗二叉树,除去根节点以外的所有的右节点添加一个右括号,左节点添加一个左括号;产生合法的左节点的条件是:左括号的数量严格大于0,产生右括号的条件是左括号剩余的数量严格的小于右括号的剩余数量。
OK那么现在有了剪枝的大致条件,我们把什么时候产生节点更加的具体一点:
- 只有当左右的括号剩余数量都严格大于0的时候才有可能产生下一个节点
- 当产生左节点的时候只需要考虑当前的左括号剩余的数量(是否严格大于0)
- 当需要产生右节点的时候,需要受到左括号的限制,也就是右括号的剩余数量需要严格的大于左括号剩余的数量
- 当我们的左右括号剩余数量都为0的时候停止
下面是Solution的过程:(三个if对应之前的三个限制条件)
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
if(n == 0) return res;
dfs("", n, n, res);
return res;
}
public void dfs(String cur, int left, int right, List<String> res){
if(left == 0 && right == 0){
res.add(cur);
return;
}
if(left > right) return;
if(left > 0) dfs(cur + "(", left - 1, right, res);
if(right > 0) dfs(cur + ")", left, right - 1, res);
}
}
同样的,这里我们使用的是左右两边的计数器做减法,使用加法同样是可以的也就不多做列举了。
虽然同样可以使用BFS来解决这个问题,但是相对于DFS来讲BFS显得没那么讨人喜,有兴趣的可以自己试验一下,但是回溯算法很多时候解决问题会比BFS更加的直观。