【算法笔记】极客时间 算法面试通关40讲 笔记  覃超

[算法笔记]极客时间 算法面试通关40讲 覃超

  • 【算法笔记】极客时间 算法面试通关40讲 覃超
    • 相关链接
    • 在leetcode 上的题号
      • 数组、链表:
      • (堆)栈stack、队列queue
      • 优先队列
      • 哈希表: map 和 set  映射和集合
      • 树,图、二叉(搜索)树
      • 二叉树的遍历
      • 递归,分治
      • 贪心 greedy
      • 广度,深度优先搜索
      • 剪枝
      • 二分查找
      • Trie树,又称 字典树 单词查找树
      • 位运算
      • 动态规划(动态地推)
      • 并查集
      • LRU cache
      • Bloom Filter

【算法笔记】极客时间 算法面试通关40讲 覃超

相关链接

  1. 课程链接(付费):https://time.geekbang.org/course/intro/130
  2. 部分PPT: [https://github.com/s32n/algorithm](https://github.com/ s32n/algorithm)
  3. 代码链接:
  4. 别人的总结:算法面试通关40讲-总结

在leetcode 上的题号

数组、链表:

206 . 反转链表 
24 . 两两交换链表中的节点 
141 . 环形链表 
142 . 环形链表 II
25 . K个一组翻转链表

(堆)栈stack、队列queue

注意:
堆是另一种数据结构,
不会让手写,每个语言都自己有实现。

栈:先入先出FILO,压栈出栈
队列:先入先出FIFO

常见数据结构操作的时间复杂度:
(栈和队列的插入删除查找的时间复杂度是相同的)

常见算法的时间复杂度如下:

【算法笔记】极客时间 算法面试通关40讲 笔记  覃超_第1张图片

20 . 有效的括号 
21 . 用栈实现队列 
225 . 用队列实现栈 

优先队列

实现机制:

  1. 用堆: 二叉堆 多项式堆 斐波那契堆
  2. 二叉搜索树

小顶堆,越小的越排在前面,最小的在最顶部。
大顶堆 同理,根节点最大。

维基百科 :heap wiki 堆

下图可知,最简单的二叉堆性能不好。严格斐波那契堆 性能较好。

【算法笔记】极客时间 算法面试通关40讲 笔记  覃超_第2张图片

703 . 数据流中的第K大元素 
239 . 滑动窗口最大值  难理解啊

使用 python 直接调用现成的heapq 堆排列实现,最小堆实现

滑动窗口问题,双端队列。

705 . 粉碎糖果  kth-largest-element-in-a-stream
706 . 滑动窗口最大值

哈希表: map 和 set  映射和集合

哈希表:映射表一样的东西,方便快速查找。
哈希函数:将所有的转(取模)到一个表中找到,表的索引通过 哈希函数计算出来。 
哈希碰撞: 数据的数量大于表的长度时,就一定会冲突,公用同一个模具。

hashmap 和 hashset :插入删除查找O(1),但存储数据无序.
treemap 和 treeset :插入删除查找O(logN),但存储数据有序.

python 字典默认的就是 hashmap 实现的。

242 . 有效的字母异位词 
1 . 两数之和 
15 . 三数之和.  难点
18 . 四个数之和

树,图、二叉(搜索)树

大概念:
树子
二叉树(面试最常见)
二叉搜索树

小概念
父亲节
孩子节点
左右孩子
根节点

二叉树
完全二叉树

链是特殊化的树
树是特殊化的图
图 地图走路径最短时会使用到。(面试很少考到)

二叉搜索树,又称为 有效二叉树、排序二叉树,空树也是二叉搜索树。具备如下性质:

  1. 左子树所有的节点的值均小于它的根节点的值。
  2. 右子树所有的结点的值均大于它的根节点的值;
  3. 递归,左右子树也都满足上面的条件。

(
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
)

注意:
不是左右孩子直接比较,还需要根节点的参与。
中序遍历后是升序的。

特点:
5. 平均的搜索插入删除的时间复杂度是 O(logN)
6. 最差时,二叉树是单边的,为O(N),称为 退化。

针对最差情况改良后的树有,
平衡二叉搜索树(如红黑树,AVL树 splay树)
他们在最坏情况下的都是O(logN)的,平均也是O(logN)

python java 中标准库实现的二叉树都是用红黑树实现的。

6 . 验证二叉搜索树  
236 . 二叉树的最近公共祖先 
235 . 二叉搜索树的最近公共祖先  

二叉树的遍历

前、中、后序遍历(实际使用的少,用的多的是深度广度优先搜索)

二叉搜索数的中序遍历 结果是 升序的。

python 递归实现如下:
【算法笔记】极客时间 算法面试通关40讲 笔记  覃超_第3张图片

递归,分治

递归是深度优先搜索的基础

递归:函数自己调用自己。

递归出口

n! 阶乘的递归实现:

def Factorial(n):
	if n == 1: return 1
	return n * Factorial(n-1)

递归模板:

# level 标记所在的层级,是否在递归中,第几层
def recursion(level, param1, param2, ....):
	# 递归出口 一般放在开始
	if level > MAX_LEVEL:  #递归出口
		print_resut
		return 
		
	# 当前层需要处理的事情 程序逻辑 业务逻辑
	process_data(level, data ...)  # 当前任务处理
	
	# 进入下一层, p1 表示给下一层的参数可能改变了
	self.recursion(level+1,p1,p2,...)  # 递归

	# 出来之后,可能需要做的一些事,不一定需要有。
	reverse_state(level)  # 可能有的后续事情

斐波那契数列:递归实现会有大量的重复,子模块。

分治算法: divide & conquer
分治可以并行计算,如果有中间结果就不能用分治,可以用动态规划。

分治算法模板:(分治算法是通过递归实现的,知识需要将问题分解成小问题)

def divide_conquer(problem, param1, param2, ..):
	# problem 当前需要处理的问题。
	# 递归出口
	if problem is None:
		print_reseut
		return
	
	#  准备当前问题需要的数据
	data = prepare_data(problem)
	# 将大问题分成小问题
	subproblems = split_problem(problem, data)

	# 分别解决小问题
	subresults1 = self.divide_conquer(subproblems[0],p1, ..)
	subresults2 = self.divide_conquer(subproblems[1],p1, ..)
	subresults3 = self.divide_conquer(subproblems[2],p1, ..)
	
	# 利用子问题的结果来解决 当前的大问题。
	result = process_result(subresult1, subresult2, result3,..)
	



50 . Pow(x, n) 
169 . 多数元素 

贪心 greedy

贪心:首选最大,最多的。解决问题的数量有限。

考的较少。

因为:当前最佳,通常不是全局最佳。

适用场景:

  1. 可分成子问题解决;
  2. 子问题的最优能推到最终问题的最优解;

与动态规划的不同:
贪心对每个子问题都做出选择,不能回退。
动态规划会 保存之间运算的结果,并根据需要选择,可以回退。

122 . 买卖股票的最佳时机 II 

广度,深度优先搜索

在树、图、状态集中寻找特定的节点。

广度优先搜索:BFS 
滴波浪的感觉,一层一层的处理。
每一次都遍历儿子。

图的广度优先搜索代码:

BFS树的时候不用 viisited,因为不会可能重复访问的情况。

广度有限搜索是非递归的,需要手动实现。

def BFS(graph, start, end):
	quece = []  # 存放BFS后的顺序
	quece.append([start])
	visited.add(start)  # 存放已经访问过的节点

	while graph: # 有内容就继续访问
		node = graph.pop()  # 拿出来
		viisited.add(node)  # 添加进去,表示已经访问过了
		
		process(node)  # 进行操作

        # 找没有被访问过的,node 的后继节点
		nodes = generate_related_nodes(node)
		queue.push(nodes)
	
	.....

没有理解这个??????代码

【算法笔记】极客时间 算法面试通关40讲 笔记  覃超_第4张图片
广度优先,人更容易理解,代码难写。
深度优先搜索,代码好些,有回溯的思想(不太好理解)。回溯根,没有可以前进的方向时,就停止。

深度有限是递归实现,代码好写。

visited =set()
def dfs(node, visited):
	visited.add(node)

	# 这里添加处理当前节点的程序
	......

 	for next_node in node.children():
 		if not next_node in visited:
 			dfs(next_node, visited)
 			

102 . 二叉树的层次遍历 
104 . 二叉树的最大深度 
111 . 二叉树的最小深度 
22 . 括号生成 

剪枝

搜索中有的分支是没有必要的,就不用去判断了。

51 . N皇后
52 . N皇后 II
36 . 有效的数独
37 . 解数独

二分查找

对数列的要求:

  1. 单调的
  2. 存在上下界
  3. 能够通过索引访问其中的元素

数组适合,链表非常不适合。

假设数列是 递增升序的,需要从其中找到 target。
时间复杂度是 O(logN)

下面的代码背下来:

left, right = 0, len(array)-1
while left <= right:
	mid = (left + right) / 2
	if array[mid] == target:
		return result
	elif array[mid] < target:
		left = mid + 1 
	else:
		right = mid + 1

69 . x 的平方根 

Trie树,又称 字典树 单词查找树

查询效率比哈希表高。
来存放字母,叶子节点存放单词。
空间换时间。

基本性质:
1 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3 每个节点的所有子节点包含的字符都不相同。

208. 实现 Trie (前缀树)
212. 单词搜索 II

位运算

191. 1的个数
231 . 2的幂
338 . 比特位计数
52 . N皇后 II

动态规划(动态地推)

  1. 递归 + 记忆(搜索) --> (动态)递推(自下向上的递推)
  2. 状态的定义:opt[n], dp[n], fib[b]
  3. 状态转移方程:opt[n] = best_of(opt[n-1], opt[n-1], …)
  4. 最优子结构

斐波那契数列,实现的过程中要记住已经计算出的结果。

def fib(n):
	return n if n<= 1 else fib(n-1)+fib(n-2)

DP vs 回溯(递归) vs 贪心

回溯(递归) - 有重复计算
贪心 - 永远局部最优
DP - 记录局部最优子结构、多种记录值

63 . 不同路径 II
70 . 爬楼梯
120 . 三角形最小路径和
152 . 乘积最大子序列
121 . 买卖股票的最佳时机
122 . 买卖股票的最佳时机 II
123 . 买卖股票的最佳时机 III
188 . 买卖股票的最佳时机 IV
714 . 买卖股票的最佳时机含手续费
309 . 最佳买卖股票时机含冷冻期
300 . 最长上升子序列
522 . 最长特殊序列 II
72 . 编辑距离

并查集

200 . 岛屿数量
547 . 朋友圈

LRU cache

146 . LRU缓存机制

Bloom Filter

你可能感兴趣的:(就业)