【算法题解】38. 括号的生成

news/2024/11/15 3:46:37/

这是一道 中等难度 的题
https://leetcode.cn/problems/generate-parentheses/

题目

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

示例 1:

输入:n = 3 
输出:["((()))","(()())","(())()","()(())","()()()"] 

示例 2:

输入:n = 1 
输出:["()"] 

提示:

  • 1 < = n < = 8 1 <= n <= 8 1<=n<=8

递归解法一

分两步操作,先递归生成所有可能的组合,然后判断组合中的每个值是否合法有效。

生成所有的组合
数字 n 代表生成括号的对数,那么最终生成的每一个结果中都会包含 2n 个括号。

12n 这些位置(或者说从 02n - 1 这些下标)上,每个位置的取值都是 “(” 或者 “)”

那么 n 对括号的的所有组合 g e n e r a t e A l l ( 2 n ) generateAll(2n) generateAll(2n) 就应该是在 g e n e r a t e A l l ( 2 n − 1 ) generateAll(2n - 1) generateAll(2n1) 的基础上再加上最后一个位置的取值,即 g e n e r a t e A l l ( 2 n − 1 ) + “ ( ” generateAll(2n - 1) + “(” generateAll(2n1)+( g e n e r a t e A l l ( 2 n − 1 ) + “ ( ” generateAll(2n - 1) + “(” generateAll(2n1)+( 的合集。

i余数 【算法题解】有效的括号

以此类推,直到遇到边界条件 n = 0 时,返回 g e n e r a t e A l l ( 0 ) generateAll(0) generateAll(0) = {“”};

判断是否是有效的括号
判断括号是否有效可以使用栈的思路,遇到 “(” 则入栈,遇到 “)” 就将栈顶元素出栈并判断是否是“(”,如果不是那么肯定不合法直接返回 false

最后如果栈中还有没出栈的元素,那么也不是合法的组合,因为没出栈的 “(” 没有与之相对应的 “)”

相关题解有: 【算法题解】14. 有效的括号

Java 代码实现

class Solution {public List<String> generateParenthesis(int n) {if(n == 0){return Arrays.asList("");}// 1. 生成所有可能的组合// 2. 排除不合法的组合List<String> all = generateAll(2*n);return all.stream().filter(str -> isValid(str)).collect(Collectors.toList());}private List<String> generateAll(int size){if(size == 0){return Arrays.asList("");}List<String> all = new ArrayList();List<String> lastAll = generateAll(size - 1);for(String last : lastAll){all.add("(" + last);all.add(")" + last);}return all;}private boolean isValid(String str){Deque<Character> stack = new LinkedList<>();char[] ch = str.toCharArray();for(int i = 0; i < ch.length; i++){if(ch[i] == '('){stack.push(ch[i]);}else if(stack.isEmpty() || stack.pop() != '('){return false;}}return stack.isEmpty();}}

Go 代码实现

func generateParenthesis(n int) []string {all := generateAll(2*n)ans := []string{}for _, str := range all {if isValid(st) {ans = append(ans, str)}}return ans
}func generateAll(size int) []string {all := make([]string, 0)if size == 0 {all = append(all, "")return all}lastAll := generateAll(size - 1)for _, last := range lastAll {all = append(all, last + "(")all = append(all, last + ")")}return all
}func isValid(s string) bool {n := len(s)stack := []byte{}for i := 0; i < n; i++ {if s[i] == ')' {if len(stack) == 0 || stack[len(stack)-1] != '(' {return false}stack = stack[:len(stack)-1]} else {stack = append(stack, s[i])}}return len(stack) == 0
}

复杂度分析

时间复杂度 O ( 2 2 n ∗ n ) O(2^{2n} * n) O(22nn), 生成所有组合的时间复杂度为 O ( 2 2 n ) O(2^{2n}) O(22n)n 为生成括号的对数。判断是否合法的时间复杂度为 O ( n ) O(n) O(n) ,总计 O ( 2 2 n ∗ n ) O(2^{2n} * n) O(22nn)

空间复杂度: O ( n ) O(n) O(n)n 为递归调用栈的深度。


递归解法二:分治

所有的合法的组合,都应该是以左括号 “(” 开头的,且后面肯定会有一个右括号 “)” 与之匹配。即所有的组合都满足 (a)b 的格式,其中 ab 可以为空或者其他任意有效的组合。

那么我们只要求的 ab 的结果,然后拼成 (a)b 就得出最终的答案了,求 ab 的结果同样是按照这个方式继续细分,还是递归的思路。

边界条件:n = 0 时,直接返回空,也可以把 n = 1 加上去,直接返回“()”

Java 代码实现

class Solution {private Map<Integer, List<String>> cache = new HashMap<>();public List<String> generateParenthesis(int n) {List<String> ans;if(cache.containsKey(n)){return cache.get(n);}if(n == 0){ans = Arrays.asList("");}else if(n == 1){ans = Arrays.asList("()");}else{ans = new ArrayList<>();// a + b = n - 1for(int a = 0; a < n; a ++){int b = n - 1 - a;List<String> aList = generateParenthesis(a);List<String> bList = generateParenthesis(b);for(String aTemp : aList){for(String bTemp : bList){ans.add("(" + aTemp +")" + bTemp);}}}}cache.put(n, ans);return ans;}}

Go 代码实现


func generateParenthesis(n int) []string {ans := []string{}if n == 0 {ans = append(ans, "")return ans;}if n == 1 {ans = append(ans, "()")return ans;}for a := 0; a < n; a++ {b := n - 1 - aaList := generateParenthesis(a)bList := generateParenthesis(b)for _, aTemp := range aList {for _, bTemp := range bList {ans = append(ans, "(" + aTemp + ")" + bTemp)}}}return ans
}

深度优先搜索

同解法一的思路一样,先生成所有可能的组合,然后再判断是否合法。不一样的是生成时使用 深度优先搜索 的思路。

递归函数:每一个位置都有 “(” 或者 “)”两个选项。

边界条件:当走到最后一个位置的时候返回。

在这里插入图片描述

关于优化剪枝:

  1. 直接以右括号 “)” 开头的肯定都不合法,直接排除。
  2. 当生成的组合中,无论是 "(" 还是 ")" 的个数已将超过一半(n个),那么肯定是不合法的了,可以提前排除掉。
  3. 当遇到 ")" 的个数 大于 "(" 的个数时,那么多出来的那个右括号已经无法匹配了,可以提前排除掉。

关于判断合法性:
只要能走到最后,且左右括号的个数都是 n,那么肯定就是合法的,无需再判断合法性。

因为不合法的几种情况都通过剪枝剪掉了:

  1. 左右括号的个数不对,已经通过 剪枝条件2 剪掉。
  2. 左右括号个数相等的情况下,只要是不合法的,其前面的某一个时刻,必然是多出来一个右括号“)”是匹配不上的,已经通过 剪枝条件3 剪掉了。如某一时刻为 "a)" ,其中 a 是合法的组合,那么 “a)” 就已经不合法了。

Java 代码实现

class Solution {private List<String> ans = new ArrayList<>();private int size;public List<String> generateParenthesis(int n) {this.size = n;char[] ch = new char[2 * n];ch[0] = '(';int left = 1, right = 0;// 第一个位置肯定是 ‘(’dfs(ch, 1, 1, 0);return ans;}private void dfs(char[] ch, int index, int left, int right){if(left > size){return;}if(right > size){return;}if(right > left){return;}// 边界条件if(index == 2 * size){ans.add(new String(ch));return;}// 每个位置都有 2 种可能ch[index] = '(';dfs(ch, index + 1, left + 1, right);ch[index] = ')';dfs(ch, index + 1, left, right + 1);return;}   
}

Go 代码实现

var (ans []stringsize int)
func generateParenthesis(n int) []string {ans = []string{}size = n// 以左括号开始path := "("dfs(path, 1, 1, 0)return ans
}func dfs(path string, index int, left int, right int){if left > size {return}if right > size {return}if(right > left){return}if index == (2 * size) {ans = append(ans, path)}dfs(path + "(", index + 1, left + 1, right)dfs(path + ")", index + 1, left, right + 1)}

剪枝后优化效果非常明显。

复杂度分析

时间复杂度 O ( 2 2 n ) O(2^{2n}) O(22n)。总的节点个数为 2 2 n + 1 − 1 2^{2n+1} -1 22n+11 个,除掉右半边,可以按照 2 2 n 2^{2n} 22n个计算。

空间复杂度: O ( n ) O(n) O(n)ch 数组长度为 2n,递归深度也是 2n


http://www.ppmy.cn/news/327205.html

相关文章

结构化命令

章节目录&#xff1a; 一、使用 if-then 语句二、if-then-else 语句三、嵌套 if 语句四、test 命令4.1 数值比较4.2 字符串比较4.3 文件比较 五、复合条件测试六、if-then 的高级特性6.1 使用单括号6.2 使用双括号6.3 使用双方括号 七、case 命令八、结束语 本章内容&#xff1…

华为OD机试真题 JavaScript 实现【最长的连续子序列】【2022Q4 100分】

一、题目描述 有N个正整数组成的一个序列&#xff0c;给定一个整数sum&#xff0c;求长度最长的的连续子序列使他们的和等于sum&#xff0c;返回该子序列的长度&#xff0c;如果没有满足要求的序列返回-1。 二、输入描述 第1行有N个正整数组成的一个序列。 第2行给定一个整…

苹果X更换电池-苹果x电池寿命80%要换吗?

手机电池寿命小于80%&#xff0c;是否需要更换电池&#xff0c;这主要取决于用户的使用体验。苹果电池更换。 我爱家电维修网告诉你如果感觉还可以&#xff0c;没有明显的使用经验&#xff0c;不一定需要立即更换&#xff0c;可以继续使用。 如果感觉寿命明显下降&#xff0c;…

苹果x面容id不可用是什么原因_iPhone X显示面容ID不可用,大神一招FaceID恢复

iPhone X显示面容ID不可用&#xff0c;大神一招Face ID恢复正常&#xff0c;解锁无忧 Face ID(面容ID)是iPhone X的安全解锁功能&#xff0c;也是iPhone X的最大亮点&#xff0c;只需要一眼就可以安全地解锁。但iPhone X面容ID不可用是实际维修中最常见的故障&#xff0c;也是这…

苹果x面容id不可用是什么原因_iPhone X使用体验!苹果手机那么保值是有原因的...

在竞争如此激烈的手机市场之下&#xff0c;苹果所要面临的压力同样巨大。在2018年第二季度的出货量报告中便可得知&#xff0c;华为已经赶超苹果&#xff0c;跃居全球第二。在众多国产手机品牌快速崛起的环境之下&#xff0c;苹果的销量更加难以稳定。不得不承认的是&#xff0…

苹果x与苹果xs的区别_这四款X系列的苹果手机怎么选择呢

X系列有4款经典机型XR&#xff0c;XS&#xff0c;X&#xff0c;XSMax 那么这四款应该怎么选呢&#xff1f; 首先&#xff0c;屏幕尺寸方面大家可以先了解下&#xff1a; 这里我以在四者中&#xff0c;价格和配置属于中间的Xs来做对比&#xff0c;能够更加清晰得对比出四款机型的…

苹果x和xs买哪个好_苹果12和苹果11哪个值得买-苹果12和11哪个更值得买

Ready 苹果iPhone 12系列产品已经开启预售&#xff0c;很多想要更换手机的伙伴都想知道&#xff0c;苹果12和11哪个更值得买呢&#xff1f; 本期视频是由iPhone 12、iPhone 11手机、iOS14系统录制的。 1.外观设计上 iPhone 11采用了圆润凸出的金属中框设计&#xff0c;而iPhone…

nginx的开始(一)---nginx的安装

文章目录 1.nginx是什么&#xff1f;2.nginx安装2.1.安装准备&#xff1a;2.2.进行安装&#xff1a;2.2.1.apt安装&#xff08;快速&#xff09;2.2.2.源码安装 2.3.配置文件简解&#xff08;nginx.conf&#xff09; 1.nginx是什么&#xff1f; Nginx&#xff08;发音为"e…