这是我参与11月更文挑战的13天,活动详情查看:2021最后一次更文挑战」。
前言
一直都计划学习数据结构与基本算法,但是平时都看一阵停一阵。现在决心坚持下去,我准备从LeetCode的HOT100开始,每天完成1~2道习题,希望通过这种方式养成持续学习的习惯。因为我是做iOS开发的,主要是用Objective-C语言,最近也在学习Swift,所以本系列的题解都将使用swift语言完成,本文更新的是LeetCode中HOT100的第13题022 括号生成。
题目
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
有效括号组合需满足:左括号必须以正确的顺序闭合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
复制代码
示例 2:
输入:n = 1
输出:["()"]
复制代码
提示:
1 <= n <= 8
复制代码
分析
本题的目的生成有效的括号对的组合,这里的括号只有小括号"()",不包含其他的括号类型。n 代表生成括号的对数,所以字符串的总长度为
,并且每一个字符只能是"("和")"中的一种。
暴力法
我们可以生成所有 22n 个 '('
和 ')'
字符构成的序列,然后我们检查每一个是否有效即可。
为了生成所有序列,我们可以使用递归。长度为 n
的序列就是在长度为 n-1
的序列前加一个 '('
或 ')#### 回溯法'
。
为了检查序列是否有效,我们可以借用我们之前完成的 LeetCode之HOT100--020 有效的括号思路进行判断,只需要遍历一次即可。
这种方法的复杂度主要在于生成序列上,为 O(n*22n)。
回溯法
暴力法还有改进的余地,我们可以只在序列仍然保持有效时才添加 '(' or ')',而不是在每一位都无脑添加'(' 和 ')'。我们可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点。 如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。这样我们就可以通过递归回溯的方法直接生成符合要求的括号对,而不需要再进行判断。这样可以减少复杂度。
题解
class KLLC022 {
func generateParenthesis(_ n: Int) -> [String] {
if n == 0 {
return [String]()
}
//初始化结果集为空,当前的字符串只有1个左括号
var result = [String]()
var curStr = "("
dfs(&curStr, 1, 0, n, &result)
return result
}
//回溯法
//curStr 当前的字符串
//leftCount 当前的字符串中左括号的数量
//rightCount 当前的字符串中右括号的数量
//n 括号对数
//result 满足条件的结果集
func dfs(_ curStr:inout String, _ leftCount:Int, _ rightCount:Int, _ n:Int, _ result:inout [String]){
//如果 当前的字符串 长度为2*n,则表示字符串over,存入结果集
if curStr.count == (2 * n) {
let ans = curStr
result.append(ans)
return
}
//如果左括号数量 < n,那么当前位可以放入左括号
if leftCount < n {
//当前位存为 左括号,进行下一位的递归
curStr.append("(")
dfs(&curStr, leftCount + 1, rightCount, n, &result)
//删除当前位存入的括号,进行当前位其他可能情况的操作
curStr.removeLast()
}
//如果右括号数量 < 右括号数量,那么当前位可以放入右括号
if rightCount < leftCount {
//当前位存为 右括号,进行下一位的递归
curStr.append(")")
dfs(&curStr, leftCount, rightCount + 1, n, &result)
//删除当前位存入的括号,进行当前位其他可能情况的操作
curStr.removeLast()
}
}
}
复制代码