Python算法题集_括号生成
本文为Python算法题集之一的代码示例
题22:括号生成
1. 示例说明
-
数字
n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。示例 1:
输入:n = 3 输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1 输出:["()"]
提示:
1 <= n <= 8
2. 题目解析
- 题意分解
- 本题是计算n对括号可能生成的字符串
- 标准解法为通过递归进行回溯求解
- 回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。
- 优化思路
-
通常优化:减少循环层次
-
通常优化:增加分支,减少计算集
-
通常优化:采用内置算法来提升计算速度
-
分析题目特点,分析最优解
-
可以在回溯算法中使用堆栈方式,也可以使用切片方式
-
可以考虑存储过程值列表,逐步扩充到n对括号
-
- 测量工具
- 本地化测试说明:LeetCode网站测试运行时数据波动很大【可把页面视为功能测试】,因此需要本地化测试解决数据波动问题
CheckFuncPerf
(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块- 本题本地化超时测试用例自己生成,详见章节【最优算法】,代码文件包含在【相关资源】中
3. 代码展开
1) 标准求解【堆栈回溯】
使用列表作为缓存,使用pop
实现回溯
页面功能测试,性能卓越,超越94%
import CheckFuncPerf as cfp
class Solution:
def generateParenthesis_base(self, n):
if n == 0:
return []
result, path = [], []
def dfs_backtrack(left, right):
if right > left or left > n or right > n:
return
if left == n and right == n:
result.append("".join(path))
return
path.append("(")
dfs_backtrack(left + 1, right)
path.pop()
path.append(")")
dfs_backtrack(left, right + 1)
path.pop()
dfs_backtrack(0, 0)
return result
aSolution = Solution()
result = cfp.getTimeMemoryStr(aSolution.generateParenthesis_base, ilen)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))
# 运行结果
函数 generateParenthesis_base 的运行时间为 8185.84 ms;内存使用量为 234904.00 KB 执行结果 = 2674440
2) 改进版一【切片回溯】
使用列表作为缓存,使用切片实现回溯
页面功能测试,马马虎虎,超过48%
import CheckFuncPerf as cfp
class Solution:
def generateParenthesis_ext1(self, n):
if n == 0:
return []
self.result, self.track = [], ''
def dfs_backtrack(left, right):
if left > right:
return
if left < 0 or right < 0:
return
if left == 0 and right == 0:
self.result.append(self.track)
return
self.track += '('
dfs_backtrack(left - 1, right)
self.track = self.track[:-1]
self.track += ')'
dfs_backtrack(left, right - 1)
self.track = self.track[:-1]
dfs_backtrack(n, n)
return self.result
aSolution = Solution()
result = cfp.getTimeMemoryStr(aSolution.generateParenthesis_ext1, ilen)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))
# 运行结果
函数 generateParenthesis_ext1 的运行时间为 10389.33 ms;内存使用量为 236416.00 KB 执行结果 = 2674440
3) 改进版二【列表缓存+逐层扩充】
使用多维列表结构保存0-n对括号组合的组合列表,逐层扩充组合到n个
页面功能测试,马马虎虎,超过68%
import CheckFuncPerf as cfp
class Solution:
def generateParenthesis_ext2(self, n):
if n == 0:
return []
map_results = []
map_results.append([None])
map_results.append(["()"])
for iIdx in range(2, n+1):
tmpResult = []
for jIdx in range(iIdx):
now_list1 = map_results[jIdx]
now_list2 = map_results[iIdx-1-jIdx]
for item1 in now_list1:
for item2 in now_list2:
if item1 == None:
item1 = ""
if item2 == None:
item2 = ""
tmpItem = "(" + item1 + ")" + item2
tmpResult.append(tmpItem)
map_results.append(tmpResult)
return map_results[n]
aSolution = Solution()
result = cfp.getTimeMemoryStr(aSolution.generateParenthesis_ext2, ilen)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))
# 运行结果
函数 generateParenthesis_ext2 的运行时间为 967.22 ms;内存使用量为 233628.00 KB 执行结果 = 2674440
4. 最优算法
根据本地日志分析,最优算法为第3种方式【列表缓存+逐层扩充】generateParenthesis_ext2
本题测试数据,似乎能推出以下结论:
- 组合的回溯求解中,列表切片操作性能低于
pop
操作 - 递归为重型结构,如果能用非递归的算法直接实现,性能会好很多
ilen = 10
aSolution = Solution()
result = cfp.getTimeMemoryStr(aSolution.generateParenthesis_base, ilen)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))
result = cfp.getTimeMemoryStr(aSolution.generateParenthesis_ext1, ilen)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))
result = cfp.getTimeMemoryStr(aSolution.generateParenthesis_ext2, ilen)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))
# 算法本地速度实测比较
函数 generateParenthesis_base 的运行时间为 8185.84 ms;内存使用量为 234904.00 KB 执行结果 = 2674440
函数 generateParenthesis_ext1 的运行时间为 10389.33 ms;内存使用量为 236416.00 KB 执行结果 = 2674440
函数 generateParenthesis_ext2 的运行时间为 967.22 ms;内存使用量为 233628.00 KB 执行结果 = 2674440
5. 相关资源
本文代码已上传到CSDN,地址:Python算法题源代码_LeetCode(力扣)_括号生成
一日练,一日功,一日不练十日空
may the odds be ever in your favor ~