题目
给出 n 代表生成的括号对数,请你写出一个函数,使其能生成所有的可能的并且 有效的括号组合
算法
如果完成一件事情有多种路径,且每种路径分成若干步骤,多半可以采用"回溯"法:
- 基本思路是:"尝试搜索",一条路走不通,就返回上一节点,走另一条路
- 如果提前分析出,走该条路无效,可以跳过该分支,称"剪枝"
思路
- 画树形结构图,分析递归结构
- 递归的终点,是否可以剪枝
- 遍历: 深度优先,广度优先
代码
public class GeneParen {
深度优先
//<editor-fold desc="深度优先遍历">
@Test
public void generateParenthesis1() {
int n = NUMBER;
//存放结果集的List
List<String> results = new ArrayList<>();
if (n == 0) {
return;
}
//执行深度优先遍历
dfs("", n, n, results);
System.out.println(results);
}
private void dfs(String curStr, int left, int right, List<String> results) {
if (left == 0 && right == 0) {
results.add(curStr);
return;
}
//深度优先遍历 - 先左边
if (left > 0) {
dfs(curStr + "(", left - 1, right, results);
}
//然后再深度遍历 - 右边
if (right > 0 && right > left) {
dfs(curStr + ")", left, right - 1, results);
}
}
//</editor-fold>
广度优先
@Test
public void generateParenthesis2() {
int n = NUMBER;
if (n == 0) {
return;
}
//执行广度优先遍历
System.out.println(dfa(n));
}
/*
* 定义一个Node类,存放节点的 '左','右'括号数 和 '当前节点字符串' */
static class Node {
private int left;
private int right;
private String res;
Node(int left, int right, String res) {
this.left = left;
this.right = right;
this.res = res;
}
}
/**
* @LinkedList 非阻塞队列
* offer() 添加一个元素到队尾并返回true 如果队列已满,则返回false
* poll() '移除'并'返回'队列头部的元素 如果队列为空,则返回null
*/
private List<String> dfa(int n) {
//存放结果集
List<String> results = new ArrayList<>();
if (n == 0) {
return results;
}
//存放节点列的Queue
Queue<Node> queue = new LinkedList<>();
//放入根节点
queue.offer(new Node(n, n, ""));
//每次while循环: 接上一个 '(' 或 ')' , n对共2n次
int m = 2 * n;
while (m > 0) {
int size = queue.size();
for (int i = 0; i < size; i++) {
//取出最新节点
Node curNode = queue.poll();
assert curNode != null;
if (curNode.left > 0) {
//若满足条件,向当前节点下 接入 左子节点 -> 节点的left-1,res接左括号
queue.offer(new Node(curNode.left - 1, curNode.right, curNode.res + "("));
}
if (curNode.right > 0 && curNode.left < curNode.right) {
//若满足条件,向当前节点 接入 右子节点 -> 节点的right-1,res接右括号
queue.offer(new Node(curNode.left, curNode.right - 1, curNode.res + ")"));
}
}
m--;
}
//节点列存放结束后,将节点的res存放到结果集中
while (!queue.isEmpty()) {
results.add(queue.poll().res);
}
return results;
}
//</editor-fold>
动态规划(状态转移)
dp[i] = "(" + dp[可能的括号数] + ")" + dp[剩下的括号数]
可能的括号数: j: 0 -> i - 1;
剩下的括号数: i - j - 1;
//<editor-fold desc="动态规划">
@Test
public void generateParenthesis3() {
int n = NUMBER;
if (n == 0) {
return;
}
System.out.println(dys(n));
}
private List<String> dys(int n) {
if (n == 0) {
return new ArrayList<>();
}
//初始化, dp[0]中存放 空字符串,算上空字符串,共n + 1 个
List<String>[] dp = new ArrayList[n + 1];
List<String> dp0 = new ArrayList<>();
dp0.add(""); //不能直接赋值给dp[0],因为add的返回值是boolean
dp[0] = dp0;
for (int i = 1; i <= n; i++) {
List<String> together = new ArrayList<>();
for (int j = 0; j < i; j++) {
List<String> str1 = dp[j];
List<String> str2 = dp[i - j - 1];
for(String s1 : str1) {
for (String s2 : str2) {
together.add("(" + s1 + ")" + s2);
}
}
}
dp[i] = together;
}
//dp[1 -> n]中存放了n为任意值时 括号的情况
return dp[n];
}
//</editor-fold>
}