回溯算法:括号生成

22. 括号生成

 

本篇文章来分析「括号匹配」问题。对于「括号」相关的问题,有两种题型:判断合法性 && 括号生成

判断合法性,详情可见题目 有效的括号,这一类题目基本上是利用栈来辅助判断

而对于「括号生成」问题,就需要用到回溯算法,下面就来详细分析该问题。详情可见题目 括号生成

这里给出两种思路,一种是本人借鉴「全排列」的思路 (详情可见 排列/组合/子集 问题),另外一种是更加优化的思路

本人思路 (极其拉胯 😭)

虽然这种思路略微的拉胯,但还是要好好纪念一下滴,谁叫是自己写出来的呢!无兴趣的可以直接看下面的思路

先给出这种思路的回溯树,如下:

1

如果需要达到去重的效果,那么括号就必须有序,这个在介绍「全排列」的时候详细分析过

即然明确了思路,那么代码也就可以很好的写出!!(我们先不考虑括号的合法性,仅仅把所有非重复的排列列举出来)

上述代码把所有非重复的结果全部遍历出来了,但此时我们还没有验证括号的合法性

根据惯性思维,一开始就想着在递归回溯的过程中同时维护一个栈,来验证当前结果的合法性!

核心代码如下:

有没有发现在这两个阶段的操作具有高度的对称性!!

分析时间复杂度

根据上面的回溯树,我们可以很快的分析出时间复杂度为 O((2n)!)

而且我们的回溯树还存在很多冗余,如下图所示:

2

从图中可以明显的看到绿色和橙色部分均和自己左边的部分有重复

优化思路 (喜出望外 😊)

首先,我们的选择不再是2n个选择,这样会存在大量的冗余。假设n = 3,第一步我们在((()))六个中选择其实和在()两个中选择无差别,前者只会造成更多的冗余

所以我们现在优化的思路就是每次都在()中选择,选择2n次,我们可以大概估算一下时间复杂度为 O(22n) (仅为估算)

其次,我们还优化了一下判断合法性的策略。对于一个字符串s来说,s的任意一个子串[0...i]中,(的数量肯定大于或等于)的数量

根据上面的分析,我们可以给出代码: