算法图解 Notes
第一章
1.2 二分查找
1.2.2 运行时间
如果最多猜测的次数与列表长度相同,这被称为线性时间(linear time).
1.3 大O表示法
大O表示法指出了算法有多快.
使用大O表示法, 这个运行时间为O(n).
大O表示法指的并非以秒为单位的速度, 大O表示法让你能够比较操作数,它指出了算法运行时间的增速.
O(n), 这里的n
为操作数, 之所以称为大O表示法, 是因为操作数前有个大O.
1.3.3 大O表示法指出了最糟情况下的运行时间
简单查找的运行时间为O(n), 二分查找的运行时间为$\logn$.
1.3.5 旅行商
计算机科学领域非常著名的旅行商问题.
找到最短旅程的方案.
第2章 选择排序
2.2 数组和链表
O(n) 是线性时间.
O(1) 是常量时间.
数组在随机读取上更快.
链表在插入上更快.
2.2.4 在中间插入
2.2.5 删除
2.3 选择排序
一般 O($\frac{n^2}{2}$) 写为 O($n^2$), 这个$\frac{1}{2}$一般会省略.
1 |
|
看来pop
可以弹出任意的元素.
第3章 递归
如果使用循环, 程序的性能可能更高; 如果使用递归, 程序可能更容易理解.
3.2 基线条件和递归条件
编写递归函数, 必须告诉它何时停止递归. 因此, 每个递归函数都有两部分:
- base case, 基线条件, 指函数不再调用自己
- recursive case, 递归条件, 指函数调用自己
3.3 栈
一个重要的编程概念–call stack, 调用栈. 其只有两个操作, push, 压入和pop, 弹出.
计算机使用一个栈来表示这些内存块.
一个重要概念: 调用另一个函数时, 当前函数暂停并处于未完成状态, 该函数的所有变量的值都还在内存中.
3.3.2 递归调用栈
栈在递归中扮演着重要角色.
第4章 快速排序
Divide and Conquer, D&C, 一种递归式问题解决方法.
D&C 的工作原理:
- 找出简单的基线条件
- 确定如何缩小问题的规模, 使其符合基线条件
D&C 并非可用于解决问题的算法, 而是一种解决问题的思路.
编写涉及数组的递归函数时, 基线条件通常是数组为空或只包含一个元素.
在很多用循环解决的方案中, 可以考虑使用递归.
4.2 快速排序
工作原理:
- 从数组中选择一个元素, 这个元素被称为基准值(pivot).
- 找出比基准值小的元素以及比基准值大的元素. 这被称为分区(partitioning).
- 对这两个子数组进行快速排序.
任何元素用作基准值都可行.
归纳证明, 一种证明算法行之有效的方式, 分为两步: 基线条件和归纳条件.
4.3 再谈大O表示法
快速排序的独特之处在于,其速度取决于选择的基准值.
4.3.1 比较合并排序和快速排序
4.3.2 平均情况和最糟情况
第5章 散列表
Hashtable, 就是哈希表.
5.1 散列函数
对于同样的输入, 散列表必须返回同样的输出.
5.2 应用案例
5.2.1 将散列表用于查找
散列表能够模拟映射关系.
5,2,2 防止重复
5.2.3 用作缓存
5.3 冲突
要明白散列表的性能, 需了解什么是冲突. Collision: 给两个键分配的位置相同.
最简单的解决方法: 如果两个键映射到了同一个位置, 就在这个位置存储一个链表.
最理想的情况是, 散列函数将键均匀地映射到散列表的不同位置.
如果散列表存储的链表很长, 散列表的速度将急剧下降.
5.4 性能
在平均情况下, 无论散列表包含多少个元素, 从中获取数据所需的时间都相同, 为O(1)–常量时间.
在最糟情况下, 散列表所有操作的运行时间都为O(n)–线性时间.
为避免冲突, 需要有:
- 较低的填装因子
- 良好的散列函数
5.4.1 填装因子
计算:
$\frac{element number}{position number}$
填装因子大于1意味着商品数量超过了数组的位置数, 一旦填装因子开始增大, 你就需要在散列表中添加位置, 这被称为调整长度(resizing).
一个经验规则: 一旦填装因子大于0.7, 就调整散列表的长度.
5.4.2 良好的散列函数
良好的散列函数让数组中的值呈均匀分布.
糟糕的散列函数让值扎堆, 导致大量冲突.
第6章 广度优先搜索
Breadth-first search, BFS. 用于找出两样东西之间的最短距离.