力扣题目:#39. 组合总和
刷题时长:30min
解题方法:回溯
复杂度分析
- 时间O(n * 2^n),是一个比较松的上界,即在这份代码中,n 个位置每次考虑选或者不选,如果符合条件,就加入答案的时间代价。但是实际运行的时候,因为不可能所有的解都满足条件,递归的时候我们还会剪枝,所以实际运行情况是远远小于这个上界的。
- 空间O(target) ,除答案数组外,空间复杂度取决于递归的栈深度,在最差情况下需要递归 O(target)层。
问题总结
- 组合重复。深度上回溯的startind不可以总从第0个元素开始,应该从广度遍历到的元素开始
本题收获
- 题目难点:题目已知集合无重复元素,解集不得含有重复组合,组合中允许元素重复出现
- 画草图:把树的广度和深度画出来有助于解题
- 剪枝优化:candidates排序后,一旦sum(path) + candidate[i] > target,无需继续遍历深度,直接跳到后一个广度
力扣题目:#40.组合总和II
刷题时长:30min
解题方法:回溯
复杂度分析
- 时间O(n * 2^n)
- 空间O(n)
问题总结
- 用set给结果去重超时,得在回溯过程中的树层中去重
- 去重条件写错,在candidates[i] == candidates[i-1]满足的情况下,还需同时满足i > startind才能在树层层面去重,而我错写成了startind > 0,导致树枝层面也被强行去重
本题收获
- 题目难点:题目已知集合内有重复元素,解集中不得含有重复组合,组合中每个元素只能出现一次。此题难点在去重逻辑。
- 通用去重技巧:初始化used数组为false,用来记录每个元素是否被使用过。当candidates[i] = candidates[i-1],若同时满足used[i-1] = True,则说明此时在树枝上遍历(深度),无需去重;若used[i-1] = False,则说明此时在树层上遍历,需要执行去重。
- continue vs return in for loop:continue只跳过剩余并进入下一个i;return直接跳出循环返回
- startind vs i in for loop:for循环里都需要用i来指代和startind的关系
力扣题目:#131.分割回文串
刷题时长:参考题解后10min
解题方法:回溯+函数判断回文串
复杂度分析
- 时间 O(n * 2^n)
- 空间O(n^2)
问题总结
- 没能抽象成回溯思路,不知如何模拟切割线
本题收获
- 题目难点:
- 切割问题可以抽象为回溯问题:枚举切割线的位置
- 如何模拟那些切割线:startindex是上一层已经确定了的分割线,i是这一层试图寻找的新分割线
- 切割问题中递归如何终止:当startindex = len(string),分割线已切到末尾
- 在递归循环中如何截取子串:string[startindex:i+1]
- 如何判断回文:双指针一首一尾判断元素是否一致