一、回溯
知识点讲解
练习:
1、排列、组合、子集相关
提示:这部分练习可以帮助我们熟悉「回溯算法」的一些概念和通用的解题思路。解题的步骤是:先画图,再编码。去思考可以剪枝的条件, 为什么有的时候用 used 数组,有的时候设置搜索起点 begin 变量,理解状态变量设计的想法。
46.全排列(中等)
47.全排列 II(中等):思考为什么造成了重复,如何在搜索之前就判断这一支会产生重复;
39.组合总和(中等)
40.组合总和 II(中等)
216.组合总和III
77.组合(中等)
78.子集(中等)
90.子集 II(中等):剪枝技巧同 47 题、39 题、40 题;
60.第 k 个排列(中等):利用了剪枝的思想,减去了大量枝叶,直接来到需要的叶子结点;
93.复原 IP 地址(中等)
2、Flood Fill
提示:Flood 是「洪水」的意思,Flood Fill 直译是「泛洪填充」的意思,体现了洪水能够从一点开始,迅速填满当前位置附近的地势低的区域。类似的应用还有:PS 软件中的「点一下把这一片区域的颜色都替换掉」,扫雷游戏「点一下打开一大片没有雷的区域」。
下面这几个问题,思想不难,但是初学的时候代码很不容易写对,并且也很难调试。我们的建议是多写几遍,忘记了就再写一次,参考规范的编写实现(设置 visited 数组,设置方向数组,抽取私有方法),把代码写对。
733. 图像渲染(Flood Fill,中等)
200. 岛屿数量(中等)
130. 被围绕的区域(中等)
79. 单词搜索(中等)
3、字符串中的回溯问题
提示:字符串的问题的特殊之处在于,字符串的拼接生成新对象,因此在这一类问题上没有显示「回溯」的过程,但是如果使用 StringBuilder 拼接字符串就另当别论。
在这里把它们单独作为一个题型,是希望朋友们能够注意到这个非常细节的地方。
17-电话号码的字母组合(中等),题解;
784-字母大小写全排列(中等);
79-单词搜索
60-排列序列
332- 重新安排行程
22- 括号生成(中等) :这道题广度优先遍历也很好写,可以通过这个问题理解一下为什么回溯算法都是深度优先遍历,并且都用递归来写。
4、游戏问题
51. N 皇后(困难):其实就是全排列问题,注意设计清楚状态变量,在遍历的时候需要记住一些信息,空间换时间;
37. 解数独(困难):思路同「N 皇后问题」;
488. 祖玛游戏(困难)
529. 扫雷游戏(困难)
二、二分法
模版
def Bisection(nums):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) >> 1
cur = nums[mid]
if cur == target:
return mid
elif cur <= target:
left = mid + 1
else:
right = mid - 1
return left
1.最大值最小化、最小值最大化类型题可以使用二分求解!!!
410.分割数组的最大值
三、优先队列
python中优先队列可以使用heapq或者queue.PriorityQueue()实现。
heapq.heapify(pq)
heapq.heappop(pq)
heapq.heappush(pq, target)
heapq.heapreplace(pq, target)
queue = queue.PriorityQueue()
queue.put() # 往队列中加元素
queue.get() # 往队列中删除元素
queue.empty() # 判断队列是否为空
1.单源最短路径问题
Python单源最短路径算法汇总
Python Leetcode网络延迟时间一题多解
Python 最短路径之存在某种限制条件下求最小值
Python Leetcode单源最短路径之两种权值下求解最小值
贝尔曼算法似乎是通用的算法?
743.网络延迟时间
787.K站中转内最便宜的航班
1928.规定时间内到达终点的最下花费
LCP 35. 电动车游城市
四、差分
1893、检查是否区域内所有整数都被覆盖
五、并查集
代码模板:
class UnionFind:
def __init__(self, n):
self.father = list(range(n))
self.num_of_sets = n
def find(self, i):
if self.father[i] == i:
return i
self.father[i] = self.find(self.father[i]) # 具有路径压缩的功能
return self.father[i]
def is_connected(self, i, j):
return self.find(i) == self.find(j)
def merge(self, i, j):
x, y = self.find(i), self.find(j)
self.father[x] = y
self.num_of_sets -= 1
547.省份数量
1202.交换字符串中的元素
1391.检查网格中是否存在有效路径
139.单词拆分
六、BFS
130. 被围绕的区域
双端BFS
代码模板:(127单词接龙)
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
wordList = set(wordList)
if endWord not in wordList:
return 0
first, second = {beginWord}, {endWord}
dist = 1
while first:
dist += 1
temp = set()
for word in first:
for i, w in enumerate(word):
for j in range(26):
cur_w = word[:i] + chr(ord('a') + j) + word[i + 1:]
if cur_w == w:
continue
if cur_w in second:
return dist
if cur_w in wordList:
temp.add(cur_w)
wordList.remove(cur_w)
first = temp
if len(first) > len(second):
first, second = second, first
return 0
另外与之类似的题:
126.单词接龙II
七、字典树
模板
class Trie(object):
def __init__(self):
self.root = {}
self.end_of_word = '#'
def insert(self, word):
node = self.root
for char in word:
if char not in node:
node[char] = {}
node = node[char]
node[self.end_of_word] = self.end_of_word
# 查找某个单词是否存在
def search(self, word):
node = self.root
for char in word:
if char not in node:
return Fasle
node = node[char]
return self.end_of_word in node
# 判读某个单词是否为一个前缀
def starts_with(self, prefix):
node = self.root
for char in prefix:
if char not in node:
return Fasle
node = node[char]
return True
题型
648.单词替换