KK's blog

每天积累多一些

0%

算法思路:

图用邻接表为输入,思路递归实现, 还要一个机制记录节点访问过没有,可以用HashSet,同时它作为结果存储BFS访问结果。
BFS多用于找最短路径 DFS多用于快速发现底部节点和具体路劲问题(如路径和或打印路径)。

BFS优缺点: 同一层的所有节点都会加入队列,所以耗用大量空间 仅能非递归实现 相比DFS较快,空间换时间 适合广度大的图

DFS优缺点: 无论是系统栈还是用户栈保存的节点数都只是树的深度,所以空间耗用小 有递归和非递归实现 由于有大量栈操作(特别是递归实现时候的系统调用),执行速度较BFS慢 适合深度大的图

如果BFS和DFS都可以用,建议用BFS,因为工业应用中,BFS不用有限的栈空间,可以利用到所有内存。

图DFS

算法步骤:

  1. 不合法情况(已访问、越界、trivial情况等)返回。
  2. 标记为已访问。
  3. 递归访问相邻节点。
  4. DFS路径尽量记录在数组中而非ArrayList中,路径(图)再DFS后要恢复为原状态L332。

Python代码:

1
2
3
4
5
6
7
def dfs(self, graph, start, visited, res):
if start in visited:
return
visited.add(start)
res.append(start)
for node in graph[start]:
self.dfs(graph, visited, node, res)

字符型DFS/组合

跟填位法相同的地方:

  1. 终止条件一样len(path)==TARGET, res.append(list(path)), return
  2. API一样:dfs(nums, start, path, result, visited[optional]), 4个参数 不同的是,组合模板是的下一轮递归用i + 1, 但这里由于是填位,一位一位加入,所以是start + 1

注意事项:

  1. 输入值为空的情况或空节点
  2. 求得解以后复制path到result中
  3. 终止条件记得return
  4. 每次递归完恢复输入图或中间结果path的状态,递归式用i + 1

Python代码:

1
2
3
4
5
6
7
8
def dfs(self, input, start, TARGET, path, result): 
if len(path) == TARGET:
result.append(list(path))
return
for i in <func>(input[start]):
path.append(i)
self.dfs(input, i + 1, TARGET , path, result)
path.pop()

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# char dfs (type 2 linear) Leetcode 77 Given two integers n and k, return all possible combinations of k numbers out of the range [1, n]
def combine(self, n: int, k: int) -> List[List[int]]:
nums = [i + 1 for i in range(n)]
path, res = [], []
self.dfs2(nums, 0, k, [], res)
return res

def dfs2(self, nums, start, k, path, res):
if len(path) == k:
res.append(list(path))
return
for i in range(start, len(nums)):
path.append(nums[i])
self.dfs2(nums, i + 1, k, path, res)
path.pop()
另一个例子:LeetCode 093 Restore IP Addresses

填位法

跟字符型DFS/组合相同的地方:

  1. 终止条件一样len(path)==TARGET, res.append(list(path)), return
  2. API一样:dfs(nums, start, path, result, visited[optional]), 4个参数 不同的是,组合模板是的下一轮递归用i + 1, 但这里由于是填位,一位一位加入,所以是start + 1

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
def dfs(self, n, start, path, res, col_set):
if len(path) == TARGET:
res.append(list(path))
return
for i in range(n):
if not self.is_valid(start, i, col_set):
continue
path.append(i)
col_set.add(i)
self.dfs(n, start + 1, path, res, col_set)
col_set.remove(i)
path.pop()

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# filling dfs (type 3) LeetCode 017 Letter Combinations of a Phone Number 
def letterCombinations(self, digits: str) -> List[str]:
if not digits:
return []
path, res = [], []
self.dfs3(digits, 0, path, res)
return res

def dfs3(self, digits, start, path, res):
if len(path) == len(digits): # len(path) == TARGET
res.append("".join(path))
return
keys = DIGIT2CHAR[digits[start]]
for letter in keys:
path.append(letter)
self.dfs3(digits, start + 1, path, res) # remember start + 1
path.pop()

Catalan法(双边递归)

DFS中几乎是最难的类型。考得很少。主要思想是左右儿子递归结果叉乘,再将所有结果返回。所以返回值一定是list

注意事项:

  1. 返回结果是解的集合,所以终止条件返回也需要是一个list
  2. 循环中除掉自己,递归左部分和右半部分,叉乘它们的结果
  3. 与记忆性搜索的模板类似,有时需要和它一起用,见LeetCode 241 Different Ways to Add Parentheses

Python代码:

1
2
3
4
5
6
7
8
9
def dfs(self, input):
if <终止条件>:
return [] # remember to use list
res = []
for i in range(len(input)):
left_res = self.dfs(input[:i])
right_res = self.dfs(input[i + 1:])
res += [<calculate results> for _l in left_res for _r in right_res]
return res

时间复杂度Catalan数为O(C[n] += C[i-1]*C[n-i]),空间复杂度O(1)

记忆性搜索(单边Catalan)

见记忆性搜索

例子:

LeetCode 140 Word Break II
这是单边catalan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
wordSet = set()
for word in wordDict:
wordSet.add(word)
cache = {}
res = self.dfs(s, wordSet, cache)
return [] if len(res) == 1 and not res[0] else res

def dfs(self, s, wordSet, cache):
if not s:
return ['']
if s in cache:
return cache[s]
res = []
for i in range(len(s)):
if s[:i + 1] not in wordSet:
continue
li = self.dfs(s[i + 1:], wordSet, cache)
for ss in li:
res.append((s[:i + 1] + ' ' + ss).strip())

cache[s] = res
return res

例子:

返回结果是解的集合,所以终止条件返回也需要是一个[None]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# catelan dfs (type 4) LeetCode 095 Unique Binary Search Trees II
# return the root of all the possible trees
def generateTrees(self, n: int) -> List[TreeNode]:
nums = [_ + 1 for _ in range(n)]
return self.dfs4(nums, 0, n)

def dfs4(self, nums, start, end): # [start, end)
if start >= end:
return [None] # remember becaues we want the 2 for-loop happen
res = []
for i in range(start, end):
left_root_nodes = self.dfs4(nums, start, i) # 0, 0
right_root_nodes = self.dfs4(nums, i + 1, end) # 1, 1
for left_root_node in left_root_nodes:
for right_root_node in right_root_nodes:
node = TreeNode(nums[i])
node.left = left_root_node
node.right = right_root_node
res.append(node)
return res

应用题型:

LeetCode 095 Unique Binary Search Trees II
LeetCode 241 Different Ways to Add Parentheses LeetCode 761 Special Binary String

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* graph: 邻接表
* visited: 记录已访问节点,避免重复访问
* start: DFS的根节点
*/
public void dfs(HashMap<Integer, LinkedList<Integer>> graph, HashSet<Integer> visited, int start){
if(visited.contains(start)){
return;
}
visited.add(start);
System.out.print(start+",");
for(Integer child : graph.get(start)){
dfs(graph, visited, child);
}
}

算法分析:

时间复杂度为O(n),h为树的高度,空间复杂度O(h),如果用系统栈,可理解其为O(1)。

算法思路:

最小堆可以维持堆顶元素为最小值。

应用:

  1. 求数组第k个大的数

Python代码:

1
2
3
4
5
6
7
8
9
def min_heap(self, nums: List[int], k: int) -> List[int]:
if not nums:
return []
res = []
for i in range(len(nums)):
heappush(res, nums[i])
if len(res) > k:
heappop(res)
return res

此法不用与堆顶元素比较,写法更简单,复杂度基本一样

max heap的话,入堆的数转负数,出堆后转为整数

1
2
3
4
5
6
7
8
def max_heap(self, nums: List[int], k: int) -> List[int]:
res = []
for i in range(len(nums)):
heappush(res, -nums[i])
if len(res) > k:
heappop(res)
res = [-n for n in res]
return res

旧法不推荐

1
2
3
4
5
6
7
8
9
10
from heapq import heapreplace, heappush
def min_heap(self, nums: List[int], k: int) -> List[int]:
res = []
for i in range(len(nums)):
if i < k:
heappush(res, nums[i])
elif nums[i] > res[0]:
heapreplace(res, nums[i])

return res

max heap的话,入堆的数转负数,跟堆顶比较的大于号不变,出堆后转为整数
注意第6行res[0]并没有负号,因为res已经是负数

1
2
3
4
5
6
7
8
9
def max_heap(self, nums: List[int], k: int) -> List[int]:
res = []
for i in range(len(nums)):
if i < k:
heappush(res, -nums[i])
elif -nums[i] > res[0]:
heapreplace(res, -nums[i])
res = [-n for n in res]
return res

BFS + Heap

本质是图,求点或点的和或线段路径的最值
这是单源最短路径Dijkstra的典型应用。Dijkstra是每条边的权重(距离), 而不是节点个数最短(BFS模板)。
区别是

  1. 用BFS distance模板。queue变成heap每个点总weight的最小堆
  2. 遍历时候每个元素多一个weight
  3. visited不再是记录这个节点访问过没有,因为节点可以多次访问,目标是找到这个节点权重最小的路径。visited记录每个节点的最小权重(路径). visited的处理在neighbor的for循环中,比较权重是否最小,如果不是就跳过。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def bfs(self, graph, start, target) -> List[int]:
    heap = [(0, k)] # weight, node
    visited = {k: 0}
    while heap:
    weight, node = heapq.heappop(heap)
    for neighbor, _weight in graph[node]:
    if neighbor in visited and visited[neighbor] <= weight + _weight:
    continue
    heapq.heappush(heap, (weight + _weight, neighbor))
    visited[neighbor] = weight + _weight
    return max(visited.values()) if len(visited) == n else -1
    BFS的distance模板(用于对比加强对BFS+Heap模板的理解)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def bfs_layer_v2(self, graph, start, target) -> int:
    queue = deque([(start, 1)])
    visited = {start}
    while queue:
    node, distance = queue.popleft()
    if node == target:
    return distance
    for neighbor in graph[node]:
    if neighbor in visited:
    continue
    queue.append((neighbor, distance + 1))
    visited.add(neighbor)
    return -1

算法分析:

时间复杂度为O(nlogk),空间复杂度O(1)

例子:

LeetCode 743 Network Delay Time
求从某一个点出发的所有能到达的点中的最短时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
# build graph
graph = collections.defaultdict(list)
for e in times:
graph[e[0]].append((e[1], e[2]))
heap = [(0, k)] # weight, node
visited = {k: 0}
while heap:
weight, node = heapq.heappop(heap)
for neighbor, _weight in graph[node]:
if neighbor in visited and visited[neighbor] <= weight + _weight:
continue
heapq.heappush(heap, (weight + _weight, neighbor))
visited[neighbor] = weight + _weight
return max(visited.values()) if len(visited) == n else -1

LeetCode 787 Cheapest Flights Within K Stops
求只允许停k个站情况下,最便宜机票价格
weight在这里是每条边的价格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
graph = collections.defaultdict(list)
for pair in flights:
graph[pair[0]].append((pair[1], pair[2]))
heap = ([(0, src, 0)]) # price, node_id, distance
visited = {(src, 0): 0} # (node, distance): price
while heap:
p, node, dist = heapq.heappop(heap)
if node == dst:
return p
if dist == k + 1:
continue
for neighbor, _price in graph[node]:
if (neighbor, dist + 1) in visited and visited[(neighbor, dist + 1)] <= p + _price:
continue
heapq.heappush(heap, (p + _price, neighbor, dist + 1))
visited[(neighbor, dist + 1)] = p + _price
return -1

更简洁的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
graph = collections.defaultdict(list)
for pair in flights:
graph[pair[0]].append((pair[1], pair[2]))
heap = ([(0, src, 0)]) # price, node_id, distance
visited = {}
while heap:
p, node, dist = heapq.heappop(heap)
if node == dst and dist <= k + 1:
return p
if node in visited and dist >= visited[node]:
continue
visited[node] = dist
for neighbor, _price in graph[node]:
heapq.heappush(heap, (p + _price, neighbor, dist + 1))
return -1

算法思路:

hasNext, next都涉及计算下一个元素,大部分题目是先计算再返回。只有BST是先返回再计算,因为BST的下一个节点取决于要返回的节点,若返回了,就无法知道下一个节点。

注意事项:

  1. hasNext中,while循环找到下一个符合条件的元素
  2. next中取值后指针要后移

Python代码:

1
2
3
4
5
6
7
8
9
10
11
def __init__(self):
self.stack = []
<add to stack>

def next(self) -> int:
return self.stack.pop() if self.hasNext() else None

def hasNext(self) -> bool:
while <until find the next element>:
<calculate>
return <next element>

例子:

LeetCode 341 Flatten Nested List Iterator
实现Nested List的Iterator。Nested List是NestedInteger的数组,NestedInteger可以是int,也可以是Nested List
nestedList = [NestedInteger]
NestedInteger 用isInteger()来判断
-> 2 by getInteger()
-> [2, 3] by getList()

1 记得在next和hasnext去pop不是stack[-1]

  1. Iterator题目都是用Stack,比如BST Iterator
  2. next或hasNext要迭代到第一个integer为止
  3. hasNext和next要对第三步保持一致: next要call self.hasNext()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class NestedIterator(TestCases):

def __init__(self, nestedList: [NestedInteger]):
self.stack = []
for n in reversed(nestedList):
self.stack.append(n)

def next(self) -> int:
return self.stack.pop() if self.hasNext() else None


def hasNext(self) -> bool:
while self.stack and not self.stack[-1].isInteger():
n = self.stack.pop()
for m in reversed(n.getList()):
self.stack.append(m)
return self.stack

应用题型:

LeetCode 251 Flatten 2D Vector
LeetCode 281 Zigzag Iterator
LeetCode 341 Flatten Nested List Iterator
LeetCode 173 Binary Search Tree Iterator

算法分析:

next操作时间复杂度为O(N + V)/NO(1),N为所有数,V为复合结构数

常用知识点

类型 函数名 作用 输入参数 返回值 例子
for range 和len结合使用相当于取某范围List下标,第三个参数为步长 N/A N/A **for i in range(len(nums))**前闭后开,用逗号
for in 枚举列表的每个数 N/A N/A for n in nums
for range 逆序遍历 N/A N/A for i in range(len(nums) - 1, -1, -1)前闭后开, 或用reversed(range(len(nums)))
for enumerate 枚举List的下标和数值,与range不同的是它不能指定范围和步长 N/A N/A for i, n in enumerate(nums)
Math max/min 取最大值 Num Num max_len = max(2, 3) 或min(1, 2, 3)
Math pow 求幂值 Num Num val = pow(2, 3)
Math int str转int或将float变成int String Num val = int('123'), val = int(2.6) -> 2
Math math.ceil/floor 求向上或下取整 Num Num import math, val = math.ceil(5/2), math.floor(5/2)
Math float('inf') 无穷大值 N/A Num val = float('inf') or float('-inf')
Math min 求数组中的正数的最小值 N/A Num min(n for n in nums if n > 0)
Math len 求数组中的正数的个数 N/A Num len([n for n in nums if n > 0])
Math sum 求数组和 N/A Num sum(nums)
Math abs 求绝对值 Num Num abs(-1)
Math //与int 求整数值 Num Num //是比它小的整数如-2.8就是-3,只在负数是有区别。写程序用的更多是**int(num / a)**结果是-2,这和Java一致
String str 任意类型变成字符串 Any String str(2)
String replace 替换 String String 'abc'.replace('c', 'd')
String format 格式化字符串 Any String f"{a} of {b}" 或者 '{} of {}'.format(a, b)
String [] 得到某一个字符 N/A Char s[2]
String [] 得到子串(前闭后开) N/A string s = 'abc', s[:2] -> 'ab', s[2:] -> 'c'
String len 得到字符串长度 N/A int num = len(s)
String [::-1] 反转字符串 N/A String new_str = s[::-1]
String [:-1] (DFS中)去掉最后一个字符 N/A String path = path[:-1]
String N/A 两个字符串叉乘 N/A List a_list = [s + c for s in s1 for c in s2]
String > 比较两个字符串 N/A boolean '111' > '21' -> False, '21' == '21' -> True
String index/find 查找返回下标 str str 'word'.index('w', 2).从下标2开始找. 'word'.find('w') 若找不到返回-1.
String upper 变成全部大写 str str 'word'.upper()
String lower 变成全部大写 str str 'WORD'.lower()
String startswith 是否以某个字母开头 str Boolean 'word'.startswith('w')
String endswith 是否以某个字母结尾 str Boolean 'word'.endswith('d')
String strip, lstrip, rstrip 去掉空格 N/A str 'word '.strip(), 'word '.lstrip(), 'word '.rstrip()
String split 用分隔符变成List str List 'a,b'.split(',')
String + 一个个字符加到字符串末 N/A N/A res += 'g'
String in 查看某个字符是否属于一个字符集合里 N/A bool if char in '([{' 可以避免用list
String isalpha 是否字母 N/A bool 'ab'.isalpha()
String isdigit 是否数字 N/A bool '23'.isdigit()
String ascii_lowercase a-z的所有字母 str str import string <br>string.ascii_lowercase
String ord 一个字母的ASCII str str ord('A') = 65
String eval 计算字符串的数值,变量,函数运算结果,返回数字 str Num eval('5+ 6') = 11, num = 1, eval(num + 1) = 2, eval('f(2)')
String ljust 靠左右边补空格 str str line.ljust(maxWidth), line.ljust(maxWidth, '#')
List [] 初始化列表 N/A N/A li = [],这是空列表,不能通过索引取值
List [] 初始化固定大小的列表 N/A N/A li = [0] * 10, [None] * 10 -> 创建一个list, * 是复制只能用于标量,若矢量是复制ref如[[]] * 10, 要用**[[] for _ in range(10)]**
List len 大小 N/A int num = len(li)
List append 尾部加入 Any N/A li.append('apple')
List insert 头部加入 Any N/A li.insert(0, 'apple')
List pop 头部/某位置删除 Any N/A li.pop(0), li.pop(2)
List remove 按值删除 Any N/A li.remove(3)
List + 两个list合并 N/A N/A list1 + list2
List extend 两个list合并到第一个 Any N/A li.extend(['apple', 'banana'])
List join 加入分隔符 List String ','.join(li), 将字符列表变成字符串''.join(char_list)
List sort 原地排序,如果List的元素是tuple,按第一个元素排序 List N/A li.sort(), 如果不改变原数组用list(li)先复制. word不支持sort(), 要先转成list(word)字母列表
List sort 按字符串长度降序排列 List N/A words.sort(key=len, reverse=True), intervals.sort(key=lambda x: x[0]), 按两个key来排序li.sort(key=lambda x: (x[0], x[1]))先顺序再逆序li.sort(key=lambda x: (x[0], -x[1]))
List sorted 非原地排序 List N/A nums[idx+1:] = sorted(nums[idx+1:]), 单词排序sorted(word)返回排好序的字母列表,要"".join(sorted(word))
List list 复制list List List new_list = list(old_list)
List list 扩展一倍一样的list List List new_list = nums * 2
List [] 反转list List List new_list = li[::-1]跟反转string一样,反转子列表nums[idx+1:] = nums[len(nums)-1:idx:-1]
List [-1] 返回list中最后一个元素 T int li[-1],而不是dp[len(li)-1]广泛用于DP题
List index list中找某个元素 T int li.index(2), 找不到的话抛出异常,先要检查if 9 in li2 else -1. 字符串用find
List count list中计算某个元素值的个数 T int li.count('a')
List [:] 批量替换成list中某些元素 N/A N/A nums[start:end + 1] = res
List [::-1] reverse sublist N/A N/A nums[:k] = nums[:k][::-1]先取sublist再倒转
deque deque 初始化队列 N/A N/A from collections import deque<br> queue = deque()<br> queue = deque([node])
deque append 入列 T N/A queue.append(node)
deque popleft 出列 Num T s = queue.popleft()
deque appendleft 队首入列 Num T s = queue.appendleft()较少用
deque [0] 看列首元素 N/A T queue = [], s = queue[0]
List(Heap) heapify 对list最小堆排序 List N/A from heapq import heapify, pq = [2, 3], heapify(pq) 若最大堆,则将所有值取负加入堆
List(Heap) heappush 入堆 List, T N/A from heapq import heappush<br> heappush(pq, 4)
List(Heap) heappop 出堆 List T from heapq import heappop<br> heappop(pq)
List(Heap) heapreplace 置换堆 T N/A heapq.heapreplace(pq, 4)= heappush(pq, 4) + heappop(pq)
List(Heap) [0] 看堆顶元素 N/A T pq[0]
List(Stack) append 入栈 T N/A stack = [], stack.append(node)
List(Stack) pop 出栈 N/A T stack = [], s = stack.pop()
List(Stack) [] 看栈顶元素 N/A T stack = [], s = stack[-1]
Dictionary {} 初始化字典 N/A N/A di = {}, di = {1: 'a', 2: 'b'}
Dictionary {} 初始化字典带初始key N/A N/A di = collections.defaultdict(int), **defaultdict(list)**避免第一次赋值时需要写if语句,一定用。只有用下标di[2]才会产生key和value,其他2 in di, di.keys()都不会产生key. collections.defaultdict(lambda: [0, 0])用于value是pair, 默认返回1: collections.defaultdict(lambda: 1)
Dictionary [] 获得字典的值 T T di[key]
Dictionary [] 插入到字典 T T di[key] = 2
Dictionary items Dict的所有pairs N/A K,V for k, v in di.items()
Dictionary items Dict的所有keys N/A List for k in di.keys(), 类型不是List,若要,则list(di.keys())
Dictionary items Dict的所有values N/A List for k in di.values(), 类型不是List,若要,则list(di.values())
Dictionary in 是否含有某个key N/A boolean if key in di
Dictionary pop 删除某个key N/A N/A di.pop(key)
Set {} 产生一个Set N/A Set b = set(), b = set(['a', 'b'])
Set add set增加一个元素 T N/A b.add('c')
Set remove set删除一个元素 T N/A b.remove('c')
Set set List转换成Set或反之 N/A N/A s = set(l), l = list(s)
bisect bisect 二分法查找下标插入位置 Num Num 跟greater_position一样, from bisect import bisect, bisect([], 3) -> 0, bisect([2], 3) -> 1, bisect([3], 3) -> 1, bisect([4], 3) -> 0。还可以用于统计小于等于target的个数,如bisect([0, 1], 1) -> 2
bisect bisect_left 二分法查找下标插入位置 Num Num 似于greater_or_equal_position但若equal时,取第一个,但greater_or_equal_position是取最后一个. 用于LIS
bisect insort 二分法查找下标插入位置且插入 Num Num 似于greater_position,插入复杂度为O(n)
bisect bisect 二分法查找二维数组 Num Num bisect.bisect([[0, 1], [0, 2], [1, 2], [1, 3]], [1]) -> [1, 2]下标,若找[1, 2] -> [1, 3]下标
Lambda func/expr...for...in 整型数组变字符串数组 List List [str(x) for x in list]
Others nonlocal N/A N/A N/A nonlocal i. 用于内部函数内的变量,类似于全局变量,不需要作为参数传入到内部函数且可以修改不用返回。见key value store
Others @dataclass N/A N/A N/A 省略__init__,__eq__等函数。<br>from dataclasses import dataclass <br>@dataclass <br>class Product: <br>  name: str
Others return 返回tuple值 N/A N/A return a, b 不要括号
Others isinstance 判断输入是什么类型 N/A bool isinstance(stack[2], list)
Others Counter 计算List和字符串频率 List dict from collections import Counter<br> di = Counter(nums)<br> di = Counter('apple')<br> graph = Counter(c for word in words for c in word)<br> Counter({c: [] for word in words for c in word})
Others [] 初始化NxM矩阵为0 N/A N/A a = [[1] * M] * N不能复制矢量。[[0 for _ in range(M)] for _ in range(N)] 先col再row
Others randint 求[start, end]之间随机值 Num Num import random<br> random.randint(0, 1) -> 0/1
Others map, max 求矩阵最大值 [][] T max(max(row) for row in matrix)
Others or 若0返回1 N/A N/A a or 1 -> a if a != 0, 1 if a == 0
Others or 若None返回空数组 N/A N/A for child in root.children or []
OrderedDict OrderedDict 类似于LinkedHashMap,dict的key的插入顺序排序 N/A N/A di = collections.OrderedDict()<br> di['a'] = 2
re search search只match一次 str str re.search(r'(\+|\-)?\d+', '-23.1e2').group() -> -23
re sub 全部用regex替代类似Java中的replaceAll str str re.sub(r'\s+', '', 'a 6 7') -> a67
Exception Exception 抛出异常 N/A N/A raise Exception('abc')

总结:
除了deque, Stack, List的实现都是List
插入append, insert
删除pop, popleft(deque)

初始化列表:
创建一个list: li = [0] * 10, [False] * 10 而不是[False * 10], [0 * 10]这是乘法
创建一个list of list不用乘号: [[0 for _ in range(M)] for _ in range(N)], [[] for _ in range(10)]

if left_val可能为整数或None,要写成if left_val is not None,否则若left_val = 0, 会False i += 1没有i++
if 0 < i < len(nums) 不像Java一样,Python可以连续比较范围
所有int都是long
22//5 = 4
22/5 = 4.4

def myFunc(e):
return e['year']
cars.sort(reverse=True, key=myFunc)

由于Python的数字类型都是Numeric(自动识别为Integer, Float, Complex Numbers),所以自动变成小数,不像Java是int
数据类型

True/False
and/or/not
pass什么都不做 if a: pass

&, ^异或, ~取反(~3), |, <<, >>

lo = hi = 0 swap: a, b = b, a elif
return -res if is_negative else res
if root / if not root
if not nums 包含(None以及len(nums) == 0
Node(0)没有new
zip是将两个list的元素同步合成tuple,以短的为终结点。例如[i + j for i, j in zip(li, li2)] -> [5, 7], li = [1, 2], li2 = [4, 5, 6]
函数中更改输入list如f(list) -> list = [''],不会改变原list,必须更改任意元素: list[:] = [''] 匿名函数和重载: ListNode.__lt__ = lambda x, y: (x.val < y.val)
Python中自定义obj可以任意定义属性,所以若要给某属性赋值如node.value,一定要match类定义里边的,不是node.val = 5。

补集:
list(set(li) - set(li2))
[n for n in self.all if n not in set(li)] 并集:
list(set(li) | set(li2))
list(set(li + li2)) 交集:
list(set(li) & set(li2))
[n for n in li if n in set(li2)]

10的6次方,不能用10^6,这是异或,用1e6或pow(10, 6) self.maxDepth

用_i来表示内部使用,dict_表示与关键字不重复

三个引号就是多行comment,#是一行comment
换行用 \

Python基础
Python命名规则 Python下划线

实现易错点

  • if not nums, not s,查看input
  • BFS中popleft() ,不是pop()
  • stack[-1] 等于peek,不是stack[0]
  • it = it.next记得移动指针

常见思路

  • 用简单例子来写代码比如计算器,先写加减再实现乘除
  • 数组或者string的连续或区间问题用递减栈或者sliding Window
  • 最值用BFS(图)或heap(数值)
  • 数组累计思想
  • nonlocal. See L543

数组题

LeetCode 001 Two Sum
LeetCode 2073 Time Needed to Buy Tickets

整数题

LeetCode 007 Reverse Integer

整洁题

LeetCode 273 Integer to English Words
LeetCode 054 Spiral Matrix
LeetCode 048 Rotate Image
LeetCode 166 Fraction to Recurring Decimal
LeetCode 008 String to Integer (atoi) 合法整数
LeetCode 065 Valid Number 合法小数指数

Map

LeetCode 003 Longest Substring Without Repeating Characters

LinkedList

注意事项: [4步]

  • fake_node
  • while it or it.next
  • 删除节点.next = None
  • it = it.next

快慢指针找循环,找中点(注意中位数有1-2个, 用fast是否为None来判断) LeetCode 142 Linked List Cycle II
LeetCode 234 Palindrome Linked List
反转LL及其模板
LeetCode 206 Reverse Linked List
LeetCode 092 Reverse Linked List II
其他:
LeetCode 021 Merge Two Sorted Lists
LeetCode 002 Add Two Numbers
LeetCode 138 Copy List with Random Pointer
LeetCode 023 Merge k Sorted Lists

Stack

递减栈
适用条件: 数组元素之间大小关系(包括相等)且顺序不能变,可以动态处理(一个个处理) 求较大值 -> 递减栈
求较小值 -> 递增栈
Stack
LeetCode 503 Next Greater Element II 此题不能用向左最值,因为向右第一个较大不是最大
LeetCode 042 Trapping Rain Water 推荐向左向右最值不用stack
LeetCode 316 Remove Duplicate Letters 结果要按原顺序
LeetCode 1209 Remove All Adjacent Duplicates in String II 元素需连续(有序)
LeetCode 239 Sliding Window Maximum stream

BST的非递归遍历

BST的非递归中序,前序,后序遍历
LeetCode 272 Closest Binary Search Tree Value II
Iterator题目基本是List转Stack
LeetCode 173 Binary Search Tree Iterator

Heap

Heap
递减栈 vs heap:实现上几乎一样,详见算法知识点目录。区别在于元素间是否需要保持顺序或者是否是stream
LeetCode 347 Top K Frequent Elements
LeetCode 253 Meeting Rooms II endTime的Heap (最常见)
LeetCode 218 The Skyline Problem 高度的Heap
Heap + BFS(本质是图,求点或点的和或线段路径的最值)
LeetCode 264 Ugly Number II
LeetCode 373 Find K Pairs with Smallest Sums
LeetCode 378 Kth Smallest Element in a Sorted Matrix

数据结构

题目大多都是HashMap + LL 或 HashMap + List

<!-- raw HTML table -->

题目 LL Map 其他数据结构
LRU 按时间顺序,头删尾入 值->LL节点 N/A
First Unique 按时间顺序,任意删尾入 值->LL节点 Set存两次以上
LFU 多个时间顺序的LL 值->LL节点 第二个Map存freq-> LL的首节点以及min_freq

LeetCode 1429 First Unique Number
LeetCode 380 Insert Delete GetRandom O(1)
LeetCode 146 LRU Cache
LeetCode 460 LFU Cache
Trie
适用条件: prefix搜索和文件系统
变体:Trie+DFS, search中迭代变递归
LeetCode 588 Design In-Memory File System
LeetCode 211 Design Add and Search Words Data Structure search含TrieNode参数 + DFS
LeetCode 212 Word Search II search含TrieNode参数
Iterator
LeetCode 251 Flatten 2D Vector
LeetCode 281 Zigzag Iterator
LeetCode 341 Flatten Nested List Iterator
LeetCode 173 Binary Search Tree Iterator
数组二分法:
LeetCode 981 Time Based Key-Value Store
LeetCode 2080 Range Frequency Queries
其他:
LeetCode 155 Min Stack

Two pointers(sliding window)

同向双指针: 应用条件需要移动右指针连续直到达到target,然后移动左指针直到达不到target,梅花间竹
收缩条件固定字符种数, 若字符种树不固定,要试1-26种字符
Two pointers
LeetCode 243 Shortest Word Distance
LeetCode 209 Minimum Size Subarray Sum
LeetCode 438 Find All Anagrams in a String
LeetCode 076 Minimum Window Substring
LeetCode 340 Longest Substring with At Most K Distinct Characters 最长类型
LeetCode 395 Longest Substring with At Least K Repeating Characters 种数不固定
[LeetCode 930 Binary Subarrays With Sum] 相向双指针
Hash(value -> index, value -> count)适用条件: 数组元素之间的关系,相加相减等于target,复杂度为O(n)
LeetCode 1010 Pairs of Songs With Total Durations Divisible by 60 value -> count变体
Two points适用条件: 数组元素之间的关系,相加相减等于target,元素相等或有序,复杂度为O(n)
回文题首先考虑用相向双指针
LeetCode 125 Valid Palindrome
LeetCode 680 Valid Palindrome II

Sorting

Quick sort
Partition的应用, O(1)空间且注意循环外处理最后一部分:

  1. 双指针,i和后部分的首指针index。 2. 若nums[i]属于前部分与nums[index]交换,否则只移动i
    LeetCode 283 Move Zeroes
    LeetCode 443 String Compression
    LeetCode 075 Sort Colors
    LeetCode 696 Count Binary Substrings
    LeetCode 041 First Missing Positive
    Quick select
    Merge sort
    LeetCode 244 Shortest Word Distance II
    LeetCode 315 Count of Smaller Numbers After Self
    LeetCode 493 Reverse Pairs
    Bucket sort
    LeetCode 347 Top K Frequent Elements
    LeetCode 2080 Range Frequency Queries

Binary Search

Binary Search
LeetCode 278 First Bad Version
LeetCode 153 Find Minimum in Rotated Sorted Array
LeetCode 033 Search in Rotated Sorted Array
LeetCode 540 Single Element in a Sorted Array
LeetCode 275 H-Index II
Binary Select
适用条件:试某个数接近于最优解,全部试一遍就是*logn的复杂度
LeetCode 378 Kth Smallest Element in a Sorted Matrix
LeetCode 004 Median of Two Sorted Arrays

Tree

检查方法: None, 一个节点,两个节点(只有左或右),三个节点 nonlocal方法,不用定义全局变量见LeetCode 543 Diameter of Binary Tree
LeetCode 104 Maximum Depth of Binary Tree
LeetCode 230 Kth Smallest Element in a BST
LeetCode 114 Flatten Binary Tree to Linked List
LeetCode 105 Construct Binary Tree from Preorder and Inorder Traversal
LeetCode 314 Binary Tree Vertical Order Traversal
方法:用Map记录子节点到父节点关系等价于形成双向Tree
LeetCode 1650 Lowest Common Ancestor of a Binary Tree III
LeetCode 236 Lowest Common Ancestor of a Binary Tree
LeetCode 863 All Nodes Distance K in Binary Tree

BFS

适用条件: 可行性,最大和最小值且每个点不能重复访问
注意事项: [3步]建图没有边的节点也要加到邻接表, 输入参数不等于迭代参数, 返回-1,
图BFS
LeetCode 200 Number of Islands 连通集问题, 对所有节点BFS
LeetCode 1048 Longest String Chain 多源最长路劲,对所有节点BFS
LeetCode 1197 Minimum Knight Moves
LeetCode 133 Clone Graph
LeetCode 297 Serialize and Deserialize Binary Tree
LeetCode 127 Word Ladder
LeetCode 301 Remove Invalid Parenthesess
LeetCode 055 Jump Game
LeetCode 1654 Minimum Jumps to Reach Home
LeetCode 286 Walls and Gates 多始点一次BFS
LeetCode 490 The Maze 一组节点作为一层的BFS
LeetCode 815 Bus Routes 一组节点作为一层的BFS
[LeetCode 1293 Shortest Path in a Grid with Obstacles Elimination] 多状态BFS
拓扑排序
LeetCode 210 Course Schedule II
LeetCode 310 Minimum Height Trees
LeetCode 269 Alien Dictionary

DFS

适用条件: 种数(优先用DP,次选用DFS),可行性
图DFS/填位法
排列
LeetCode 526 Beautiful Arrangement
组合
LeetCode 039 Combination Sum
LeetCode 077 Combinations
LeetCode 216 Combination Sum III
LeetCode 017 Letter Combinations of a Phone Number 填位法
LeetCode 051 N-Queens 填位法
LeetCode 037 Sudoku Solver 填位法, 单边DFS + 循环DFS
LeetCode 093 Restore IP Addresses 分割型,结果分组型DFS
LeetCode 291 Word Pattern II 分割型, 单边DFS + 循环DFS
LeetCode 079 Word Search 图DFS + 分割型
LeetCode 126 Word Ladder II 图DFS
记忆性搜索
LeetCode 140 Word Break II 分割型
LeetCode 095 Unique Binary Search Trees II Catalan型DFS
LeetCode 241 Different Ways to Add Parentheses Catalan型DFS

DP

适用条件: 最值,种数,可行性。输入数据有序,暴力法非多项式
注意事项: [5步]多1, 初始化, 多1, 少1, 答案
DP
LeetCode 070 Climbing Stairs 单序列型
LeetCode 091 Decode Ways 单序列型
LeetCode 198 House Robber 单序列型
LeetCode 377 Combination Sum IV 数值到个数DP
LeetCode 322 Coin Change 数值到个数DP
LeetCode 518 Coin Change 2 数值到个数DP(元素无序)
LeetCode 063 Unique Paths II 坐标型DP
LeetCode 064 Minimum Path Sum 坐标型DP
LeetCode 221 Maximal Square 坐标型DP
LeetCode 174 Dungeon Game 坐标型DP
LeetCode 718 Maximum Length of Repeated Subarray 匹配型DP
LeetCode 300 LIS 匹配型DP
LeetCode 139 Word Break 匹配型DP
LeetCode 072 Edit Distance 匹配型DP
LeetCode 044 Wildcard Matching 匹配型DP
LeetCode 010 Regular Expression Matching 匹配型DP
LeetCode 516 Longest Palindromic Subsequence 区间型DP
LeetCode 312 Burst Balloons 区间型DP
Karat 002 Longest Common Continuous Subarray 打印路径
LeetCode 300 Longest Increasing Subsequence 打印路径 LeetCode 368 Largest Divisible Subset 打印路径 LeetCode 1143 Longest Common Subsequence 打印路径 多状态DP需要同时赋值
LeetCode 152 Maximum Product Subarray 多状态DP
LeetCode 926 Flip String to Monotone Increasing 多状态DP
LeetCode 256 Paint House 多状态DP 多状态DP-fg双函数(g为累计DP) - 和前状态不能相邻
LeetCode 337 House Robber III 多状态DP-fg双函数 LeetCode 309 Best Time to Buy and Sell Stock with Cooldown 多状态DP-fg双函数
LeetCode 329 Longest Increasing Path in a Matrix Floyd

其他

动态计算连接数,其他均用BFS
Union Find
LeetCode 721 Accounts Merge
[双向BFS]LeetCode 127 Word Ladder
[线段树]LeetCode 729 My Calendar I

最大的k个/最大的前k个数(无序数值数组)

LeetCode 347 Top K Frequent Elements
优先考虑Heap,因为Heap支持动态和静态数据。适用范围最广,动态包括Data stream,不将整个数组读入从而优化复杂度
LeetCode 295 Find Median from Data Stream
LeetCode 378 Kth Smallest Element in a Sorted Matrix
Binary select,数值法计算个数,不能用于动态,实现较复杂
LeetCode 004 Median of Two Sorted Arrays
Quick select, binary select的特殊版,只能用于一维数组

数组区间内动态最大值最小值或保留数组顺序(数值数组)

由于保留顺序,首选stack
LeetCode 2104 Sum of Subarray Ranges

二维坐标最短距离

BFS首选,然后DP。若状态转移是无序用BFS,否则DP(可从上到下,从左到右)
BFS
LeetCode 1197 Minimum Knight Moves
LeetCode 909 Snakes and Ladders
DP
LeetCode 174 Dungeon Game 坐标型DP

累计思想

将相连的批量计算提高效率或者满足累计条件
LeetCode 032 Longest Valid Parentheses 统计左括号个数保证其一直为非负数
LeetCode 134 Gas Station 保证gas一直为非负数
数组的presum
加入presum[0] = 0
LeetCode 696 Count Binary Substrings
LeetCode 560 Subarray Sum Equals K
LeetCode 238 Product of Array Except Self
[LeetCode 930 Binary Subarrays With Sum] 子矩阵的presum
同数组,presum矩阵大小为长度 + 1

1
2
3
res = presum[x][y] - left - top + diag

presum[i][j] = row_sum + (presum[i - 1][j] if i > 0 else 0)
LeetCode 304 Range Sum Query 2D - Immutable
LeetCode 427 Construct Quad Tree

括号题或者字符串运算题

括号题或者字符串运算题优先用Stack
LeetCode 020 Valid Parentheses
LeetCode 032 Longest Valid Parentheses
LeetCode 1249 Minimum Remove to Make Valid Parentheses 括号下标
LeetCode 678 Valid Parenthesis String
LeetCode 022 Generate Parentheses
LeetCode 071 Simplify Path
字符串运算题
LeetCode 227 Basic Calculator II
LeetCode 224 Basic Calculator
LeetCode 394 Decode String

游戏题

数据结构设计 + 模拟
[LeetCode 348 Design Tic-Tac-Toe] 设计游戏
LeetCode 051 N-Queens 求游戏所有解
LeetCode 037 Sudoku Solver 求游戏是否有解

区间重合题

条件区间重合且区间始点有序。用端点排序法(更常用)或Heap(处理重合情况下的计算)
LeetCode 253 Meeting Rooms II
LeetCode 056 Merge Intervals
LeetCode 759 Employee Free Time
LeetCode 1235 Maximum Profit in Job Scheduling
LeetCode 871 Minimum Number of Refueling Stops
LeetCode 218 The Skyline Problem 端点排序法 + Heap

所有子串和子数组

用presum, stack, DP
LeetCode 560 Subarray Sum Equals K
LeetCode 696 Count Binary Substrings
LeetCode 907 Sum of Subarray Minimums
LeetCode 2104 Sum of Subarray Ranges
Leetcode 828 Count Unique Characters of All Substrings of a Given String

26字母存储法

条件,若题目含所有字母,且求最值,需要存储每个字母对应的结果
LeetCode 395 Longest Substring with At Least K Repeating Characters
LeetCode 1857 Largest Color Value in a Directed Graph

水王法

candidate取首元素,遍历每一个元素,若candidate不符合条件就换成当前元素为candidate
LeetCode 169 Majority Element
LeetCode 277 Find the Celebrity
LeetCode 134 Gas Station

Word相关题目

DFS: word pattern, word search
BFS: word ladder
DP: word break

Free mock interview