Algorithm summary
Category
双指针
- 相向双指针
- 同向双指针
二分查找
- Red-Blue Painting Approach
链表
- Reverse
- Fast Slow Pointer。链表中点,环形链表入口
- 删除
二叉树
- DFS, Depth First Search
- BFS, Breadth First Search
- Pre-order, In-order, Post-order Traversal
- Level-order Traversal
回溯
- 子集型,选或不选
- 组合型,选哪个
- 排列型,排除已选
动态规划
- Dp principle
- Backup DP
- Linear DP
- State Machine DP
- Interval DP
- Tree DP
- etc
数据结构
- 线段树
- 树状数组
- 堆、大小顶堆
- 栈、单调栈
- 队列、优先队列
- etc
双指针
1 双向双指针
经典题目有
- 两数之和
- 目标数
- 三个目标数
- 小于某个数的连续子集
- 接雨水 Waterfall
- 也是一种前缀集的题
func twoSum(numbers []int, target int) []int {
var i int = 0
var j = len(numbers) - 1
var res = make([]int, 0)
for i < j {
var curSum int = numbers[i] + numbers[j]
if curSum < target {
i +=1
} else if curSum > target {
j -=1
} else {
res = []int{i + 1, j +1}
break
}
}
return res
2 同向双指针
回溯
1 子集型(选或不选)
Intro
- 本质上是看选不选某个元素,是一个增量构造答案的过程,这个过程适合用递归解决。
- 举例
- 构造长为n的字符串
- 枚举一个字母
- 构造长为n-1的字符串
- 回溯三问:
- 当前操作?当前的
i
元素要不要跳过 - 子问题?从下标
>=i
的数字中构造子集 - 下一个子问题?从下标
>=i+1
的数字中构造子集
- 当前操作?当前的
- 两个视角(不是很清楚为什么两个视角不同,其实在我看来都差不多)
- 输入的视角
- 答案视角
sample
Example 1:
Input: nums = [1,2,3]
Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
code
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
if n == 0: # 为空直接返回
return []
ans = [] # 答案列表
path = [] # 回溯遍历的路径
# (1) 这是从输入的角度考虑,(选或不选)。
# 遍历一颗满二叉树(即每一层的元素都是满的,所以遍历的时间复杂度实际上是2^n)
def dfs(i): # 深度遍历
if i == n: # 边界条件。当遍历到叶子节点后生成答案
ans.append(path.copy()) # 这 边使用copy()避免path地址引用改变已生成的结果,这边的copy其实也花了时间
# ans.append(''.join(path)) # 这是把string类型的数组内容join起来
return
dfs(i+1) # 跳过当前数字
path.append(nums[i]) # 添加到路径中
dfs(i+1) # 这回是把当前数字放到path里
path.pop() # 恢复现场,lol
# (2) 这是从答案的角度来考虑的(选哪个数)。每个叶子节点都是答案
# 我们可以每层都递归数组的所有元素,但要注意下一个数的下标应当大于当前数的下标。
# 比如之前添加过子 数组[1,2],接下来不应该加[2,1]了
# 也就是说递归思路如下
# - 当前元素?应该选择 j >= i 的j元素
# - 子问题?从下标 >=i 的数组中构造子集
# - 下一个子问题?从下标 >= i+1 的数组中构造子集
def dfsV2(i): # 深度遍历
ans.append(path.copy()) # 这边使用copy()避免path地址引用改变已生成的结果,这边的copy其实也花了时间
if i == n: # 边界条件。当遍历到叶子节点后生成答案
# ans.append(''.join(path)) # 这是把string类型的数组内容join起来
return
for j in range(i, n): # 枚举选中的数字
path.append(nums[j]) # 添加到路径中
dfsV2(j+1) # 这回是把当前数字放到path里
path.pop() # 恢复现场,lol
dfsV2(0) # 从零开始
return ans