【问题描述】22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
【解答思路】
- 左括号数量需要一直大于等于右数量
- 左括号数量和右括号数量小于等于总数n
- 左括号数量和右括号数均等于总数n 得出结果
1.深度遍历 DFS (回溯算法)
时间复杂度:O(N^2) 空间复杂度:O(N)
减法
import java.util.ArrayList;
import java.util.List;public class Solution {// 做减法public List<String> generateParenthesis(int n) {List<String> res = new ArrayList<>();// 特判if (n == 0) {return res;}// 执行深度优先遍历,搜索可能的结果dfs("", n, n, res);return res;}/*** @param curStr 当前递归得到的结果* @param left 左括号还有几个可以使用* @param right 右括号还有几个可以使用* @param res 结果集*/private void dfs(String curStr, int left, int right, List<String> res) {// 因为每一次尝试,都使用新的字符串变量,所以无需回溯// 在递归终止的时候,直接把它添加到结果集即可,注意与「力扣」第 46 题、第 39 题区分if (left == 0 && right == 0) {res.add(curStr);return;}// 剪枝(如图,左括号可以使用的个数严格大于右括号可以使用的个数,才剪枝,注意这个细节)if (left > right) {return;}if (left > 0) {dfs(curStr + "(", left - 1, right, res);}if (right > 0) {dfs(curStr + ")", left, right - 1, res);}}
}作者:liweiwei1419
链接:https://leetcode-cn.com/problems/generate-parentheses/solution/hui-su-suan-fa-by-liweiwei1419/
加法
import java.util.ArrayList;
import java.util.List;public class Solution {// 做加法public List<String> generateParenthesis(int n) {List<String> res = new ArrayList<>();// 特判if (n == 0) {return res;}dfs("", 0, 0, n, res);return res;}/*** @param curStr 当前递归得到的结果* @param left 左括号已经用了几个* @param right 右括号已经用了几个* @param n 左括号、右括号一共得用几个* @param res 结果集*/private void dfs(String curStr, int left, int right, int n, List<String> res) {if (left == n && right == n) {res.add(curStr);return;}// 剪枝if (left < right) {return;}if (left < n) {dfs(curStr + "(", left + 1, right, n, res);}if (right < n) {dfs(curStr + ")", left, right + 1, n, res);}}
}
** 栈实现**
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;public class Solution {class Node {/*** 当前得到的字符串*/private String res;/*** 剩余左括号数量*/private int left;/*** 剩余右括号数量*/private int right;public Node(String str, int left, int right) {this.res = str;this.left = left;this.right = right;}}// 注意:这是深度优先遍历public List<String> generateParenthesis(int n) {List<String> res = new ArrayList<>();if (n == 0) {return res;}// 查看了 Stack 源码,官方推荐使用 Deque 对象,// 注意:只使用栈相关的接口,即只使用 `addLast()` 和 `removeLast()`Deque<Node> stack = new ArrayDeque<>();stack.addLast(new Node("", n, n));while (!stack.isEmpty()) {Node curNode = stack.removeLast();if (curNode.left == 0 && curNode.right == 0) {res.add(curNode.res);}if (curNode.left > 0) {stack.addLast(new Node(curNode.res + "(", curNode.left - 1, curNode.right));}if (curNode.right > 0 && curNode.left < curNode.right) {stack.addLast(new Node(curNode.res + ")", curNode.left, curNode.right - 1));}}return res;}
}作者:liweiwei1419
链接:https://leetcode-cn.com/problems/generate-parentheses/solution/hui-su-suan-fa-by-liweiwei1419/
2. 广度遍历 BFS
时间复杂度:O(N^2) 空间复杂度:O(N)
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;public class Solution {class Node {/*** 当前得到的字符串*/private String res;/*** 剩余左括号数量*/private int left;/*** 剩余右括号数量*/private int right;public Node(String str, int left, int right) {this.res = str;this.left = left;this.right = right;}}public List<String> generateParenthesis(int n) {List<String> res = new ArrayList<>();if (n == 0) {return res;}Queue<Node> queue = new LinkedList<>();queue.offer(new Node("", n, n));while (!queue.isEmpty()) {Node curNode = queue.poll();if (curNode.left == 0 && curNode.right == 0) {res.add(curNode.res);}if (curNode.left > 0) {queue.offer(new Node(curNode.res + "(", curNode.left - 1, curNode.right));}if (curNode.right > 0 && curNode.left < curNode.right) {queue.offer(new Node(curNode.res + ")", curNode.left, curNode.right - 1));}}return res;}
}
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;public class Solution {class Node {/*** 当前得到的字符串*/private String res;/*** 剩余左括号数量*/private int left;/*** 剩余右括号数量*/private int right;public Node(String res, int left, int right) {this.res = res;this.left = left;this.right = right;}@Overridepublic String toString() {return "Node{" +"res='" + res + '\'' +", left=" + left +", right=" + right +'}';}}public List<String> generateParenthesis(int n) {List<String> res = new ArrayList<>();if (n == 0) {return res;}Queue<Node> queue = new LinkedList<>();queue.offer(new Node("", n, n));// 总共需要拼凑的字符总数是 2 * nn = 2 * n;while (n > 0) {int size = queue.size();for (int i = 0; i < size; i++) {Node curNode = queue.poll();if (curNode.left > 0) {queue.offer(new Node(curNode.res + "(", curNode.left - 1, curNode.right));}if (curNode.right > 0 && curNode.left < curNode.right) {queue.offer(new Node(curNode.res + ")", curNode.left, curNode.right - 1));}}n--;}// 最后一层就是题目要求的结果集while (!queue.isEmpty()) {res.add(queue.poll().res);}return res;}
} 作者:liweiwei1419
链接:https://leetcode-cn.com/problems/generate-parentheses/solution/hui-su-suan-fa-by-liweiwei1419/
3. 动态规划
时间复杂度:O(N^2) 空间复杂度:O(N)
import java.util.ArrayList;
import java.util.List;public class Solution {// 把结果集保存在动态规划的数组里public List<String> generateParenthesis(int n) {if (n == 0) {return new ArrayList<>();}// 这里 dp 数组我们把它变成列表的样子,方便调用而已List<List<String>> dp = new ArrayList<>(n);List<String> dp0 = new ArrayList<>();dp0.add("");dp.add(dp0);for (int i = 1; i <= n; i++) {List<String> cur = new ArrayList<>();for (int j = 0; j < i; j++) {List<String> str1 = dp.get(j);List<String> str2 = dp.get(i - 1 - j);for (String s1 : str1) {for (String s2 : str2) {// 枚举右括号的位置cur.add("(" + s1 + ")" + s2);}}}dp.add(cur);}return dp.get(n);}
}作者:liweiwei1419
链接:https://leetcode-cn.com/problems/generate-parentheses/solution/hui-su-suan-fa-by-liweiwei1419/
【总结】
1.搜索使用深度优先遍历 (回溯算法)
- 广度优先遍历 程序员自己编写节点类 显示使用队列数据结构
- 深度优先遍历 程序员使用系统栈 递归执行 系统顶栈弹出所需状态信息 无需写结点和显示使用栈
2.动态规划特点
如何设计状态(我是谁)?
状态x从哪里推过来?(我从哪里来?/我要到哪里去?)
动态规划 = 在拓扑序上做递推
自顶向上去递推 自顶向下的递归
1、自底向上:从小规模问题开始,逐渐得到大规模问题的解集;
2、无后效性:后面的结果的得到,不会影响到前面的结果。
3. 思路
DFS => 记忆化搜索 => 动态规划