第一章 算法简介
1.1 二分查找
1.2 大O表示法
- 除了最糟情况,还应该考虑平均情况的运行时间
大O运行时间 | 算法 |
---|---|
O(logn) | 二分查找 |
O(n) | 简单查找 |
O(n*logn) | 快速排序 |
O(n²) | 选择排序 |
O(n!) | 旅行商问题 |
- 算法的速度指的是操作数的增速
- 表示随着输入的增加,其运行时间将以什么样的速度增加
第二章 选择排序
2.1 数组和链表
数组 | 链表 | |
---|---|---|
读取 | O(1) | O(n) |
插入 | O(n) | O(1) |
删除 | O(n) | O(1) |
2.2 选择排序
- 两次循环
第三章 递归
3.1 递归
- “如果使用循环,程序的性能可能更高;如果使用递归,程序可能更容易理解。”
3.2 基线条件和递归条件
- 递归条件:自己调用自己
- 基线条件:不再调用自己
3.3 栈
- 调用栈(call stack)
- 两种操作:压入和弹出
- 所有函数调用都进入调用栈
第四章 快速排序
4.1 分而治之
- 分而治之(divide and conquer),D&C算法是一种解决问题的思路
- D&C包括
- 找出基线条件,这种条件必须尽可能简单
- 不断将问题分解(或者说缩小规模),直到符合基线条件
4.2 快速排序
- C语言中
qsort
实现的就是快速排序 - 快速排序使用了D&C
- 归纳证明,一种证明算法行之有效的方式
- 基线条件,对空或者一个元素是有效的
- 归纳条件,对两个元素有效,对三个元素有效…
4.3 再谈大O表示法
- 大O表示法中,n实际上是这样的
- 快速算法最快是O(nlogn),最糟糕是O(n²)
- 只要每次随机选择元素作为基准值,最佳情况就是平均情况,为O(nlogn)
- 快速算法是最快的排序算法之一,也是D&C典范
第五章 散列表
5.1 散列函数
- 将输入映射到数字
- 散列函数总是将同样的输入映射到相同的索引
- 散列函数将不同的输入映射到不同的索引
- 散列函数知道数组的大小,只返回有效的索引
- 散列表也使用数组来存储数据,因此其获取元素的速度和数组一样快
- 散列表由键和值组成
- Python提供的散列表实现为字典
- 散列表适用于
- 模拟映射关系
- 防止重复
- 缓存/记住数据,以免服务器再通过处理来生成它们
5.2 冲突
- 冲突:给两个键分配的位置相同
- 如果两个键映射到了同一个位置,就在这个位置存储一个链表,这样效率会不高,所以选择一个好的散列函数很重要
5.3 性能
- 提升性能需要:
- 较低的填装因子(散列表中有多少位置使用),一旦大于0.7就调整散列表的长度
- 良好的散列函数,SHA函数
第六章 广度优先搜索
6.1 图介绍
- 图由节点(node)和边(edge)组成
6.2 广度优先搜索
- 解决最短路径问题的算法被称为广度优先搜索,BFS
- 广度优先搜索是一种用于图的查找算法,可帮助回答两类问题
- 从节点A出发,有前往节点B的路径吗?(在你的人际网中,有芒果经销商吗?)
- 从节点A出发,前往节点B的哪条路径最短?(哪个芒果经销商与你的关系最近?)
6.3 队列
- 队列:可以按添加顺序进行操作(否则难以找到最短的路径)
- 两种操作:入队和出队
- 在Python中,使用函数
deque
来创建一个双端队列 - 队列:先进先出;栈:后进先出
6.4 实现
- 这种映射关系可以用散列表来处理
- 避免死循环,检查一个后,应进行记录(我的朋友是朋友的朋友)
- 广度优先搜索的运行时间为O(人数+边数),O(V+E),V为顶点数,E为边数
第七章 狄克斯特拉算法
7.1 狄克斯特拉算法
步骤:
- 找出“最便宜”的节点,即可在最短时间内到达的节点
- 更新该节点的邻居的开销
- 重复这个过程,直到对图中的每个节点都这样做了
- 计算最终路径
7.2 加权图
7.3 负权边
- 如果有负权边,就不能使用狄克斯特拉算法,因为狄克斯特拉算法假设:考虑过的节点,在之后不会有更短的路径到达
- 处理有负权边的图,可以使用贝尔曼-福德算法
7.4 实现
- 重要:狄克斯特拉算法只适用于有向无环图DAG
- 关键理念:找出图中最便宜的节点,并确保没有到该节点的更便宜的路径
第八章 贪婪算法
8.1 贪婪算法
- 每步都采取最优的做法,即每步都是局部最优解,最终得到的就是全局最优解
8.2 近似算法
优劣的判断标准:
-
速度有多快
-
得到的近似解与最优解的接近程度
-
集合类似于列表,只是不能包括重复的元素;可以做并集、交集和差集等运算
8.3 NP完全问题
- 元素较少时算法的运行速度非常快,但随着元素数量的增加,速度会变得非常慢
- 涉及“所有组合”的问题通常是NP完全问题
- 不能将问题分成小问题,必须考虑各种可能的情况,这可能是NP完全问题
- 如果问题涉及序列(如旅行商问题中的城市序列)且难以解决,它可能就是NP完全问题
- 如果问题涉及集合(如广播台集合)且难以解决,它可能就是NP完全问题
- 如果问题可转换为集合覆盖问题或旅行商问题,那它肯定是NP完全问题
第九章 动态规划
9.1 动态规划
- 先解决小问题,再逐步解决大问题
- 需要在给定约束条件下优化某种指标时,动态规划很有用
- 仅当每个子问题都是离散的,即不依赖于其他子问题时,动态规划才管用
9.2 注意点
- 每种动态规划解决方案都涉及网格
- 单元格中的值通常就是要优化的值,背包问题中,就是商品的价值
- 每个单元格都是一个子问题,所有需要考虑如何将问题分成子问题
9.3 绘制网格
- 单元格中的值是什么?
- 如何将这个问题划分为子问题?
- 网格的坐标轴是什么?
第十章 K最近邻算法
10.1 KNN
- 分类:编组
- 回归:预测结果(如一个数字)
10.2 特征提取
- 距离公式
- 余弦相似度
第十一章 接下来如何做
11.1 树
- 对于每个节点,左子节点的值都比它小,而右子节点的值都比它大
- 二叉树存在一些缺点,比如不能随机访问
- 处于平衡状态的特殊二叉查找树:红黑树
11.2 反向索引
- 一个散列表,将单词映射到包含它的页面,就是反向索引
- 常用于创建搜索引擎
11.3 并行算法
- 并行性管理开销:合并结果也是需要时间的
- 负载均衡:均匀地分配工作
11.4 分布式算法
- 映射(map):将一个数组转换为另一个数组
- 归并(reduce):将一个数组转换为一个元素
- MapReduce使用这两个概念在多台计算机上执行数据查询
11.5 布隆过滤器
- 涉及庞大的集合,需要判断一个元素是否属于其中
- 是一种概率型数据结构
- 得到的答案很有可能是正确的
- 占用的存储空间很少
- 适合用于不要求答案绝对准确的情况
11.6 线性规划
- 用于在给定约束条件下最大限度地改善指定的指标
- 所有图算法都可以使用线性规划来实现