一、算法简介
这的确是一本像小说一样有趣的算法入门书,讲的是一个事儿,一个理儿,没有数学公式,不像《算法设计与分析基础》那样是一本理论书。
这里记录的只是读书过程中对重要结论和关键性质的记录,关于各个算法的精髓后续讨论。
1.查找算法:二分查找,用于有序元素列表(将执行步骤从40亿减少到32个!)
2.大O表示法:
算法运行时间的增速;指出了最糟情况下的运行时间
O(log n),对数时间,如二分查找
O(n),线性时间,如简单查找
O(n*log n),如快速排序
O(n2),如选择排序
O(n!),如旅行商问题的解决方案
算法运行时间并不以秒为单位。
算法的速度指的并非时间,而是操作数的增速。
谈论算法的速度时,我们说的是随着输入的增加,其运行时间将以什么样的速度增加。
O(log n)比O(n)快,当需要搜索的元素越多时,快得越多。
二、选择排序
1.数组和链表的操作运行时间:
| 数组 | 链表 |
读取 | O(1) | O(n) |
插入 | O(n) | O(1) |
删除 | O(n) | O(1) |
数组:随机访问和顺序访问
链表:顺序访问
Facebook存储用户名,既不是数据也不是链表,而是混合结构:链表数组。
2.选择排序
遍历列表,找出最多(大)的,将其添加到一个新列表中;
再次这样做,找出第二多(大)的;
继续这样做,得到一个有序列表。
其运行时间是O(n2)。
3.总结
计算机内存由于一大堆抽屉。
数组的元素都在一起。
链表的元素是分开的,其中每个元素都存储了下一个元素的地址。
数组的读取速度很快。
链表的插入和删除速度很快。
三、递归
每个递归函数有两个条件:
基线条件:函数不再调用自己
递归条件:函数调用自己
所有函数调用都进入调用栈,调用另一个函数时,当前函数暂停并处于未完成状态。
四、快速排序
分而治之(D&C):将问题逐步分解。
编写涉及数组的递归函数时,基线条件通常是数组为空或只包含一个元素。
分土地问题:将一块土地均匀地分成方块,且分出的方块尽可能大。
求和问题:给定一个数字数组,求他们想加的结果。
快速排序方法:
1. 选择基准值;
2. 将数组分成两个子数组:一个由所有小于基准值的数字组成的子数组和一个由所有大于基准值的数字组成的子数组;
3. 对这两个子数组进行快速排序。
实现快速排序时,随机地选择用作基准值的元素。平均运行时间为O(n logn)。
五、散列表
六、广度优先搜索
七、迪克斯特拉算法
八、贪婪算法
贪婪算法是一种非常简单的问题解决策略,即每步都选择局部最优解,用于处理没有快速算法的问题。
1. 教室调度问题
2. 背包问题
3. 集合覆盖问题
4. 旅行商问题
从背包问题可知,贪婪算法并不一定得到全局最优解,但非常接近。
近似算法:速度快,且得到的近似解与最优解很接近。在有些情况下,完美是优秀的敌人。
NP完全问题:
以难解著称,需要计算所有的解,并从中选出最小/最短的那个。
识别NP完全问题:
1. 元素较少时算法运行速度非常快,但随着元素数量的增加,速度变得非常慢。
2. 涉及“所有组合”的问题通常是NP完全问题。
3. 不能将问题分成小问题,必须考虑各种可能的情况。这可能是NP完全问题。
4. 如果问题涉及序列且难以解决,它可能是NP完全问题。
5. 如果问题涉及集合且难以解决,它可能是NP完全问题。
6. 如果问题可转换为集合覆盖问题或旅行商问题,那它肯定是NP完全问题。
九、动态规划
背包问题:
可通过集合子集或排列组合方式找出共有多少种商品组合。再找出价值最高的组合。
动态规划:
工作原理是先解决子问题,再逐步解决大问题,可借助前面子问题的结果。在背包问题中,再增加一件商品时,最大值可能发生变化,但前面的子问题不受到影响。沿着表格的一列往下走,最大价值不会降低,因为每一个子问题(表格)存储的都是当前最大价值,最不济也是上面那个最大值。当行的排列顺序发生变化时,不会影响最终结果,不会因为先看到吉他后看到电脑而影响最终背包里要装的东西。在使用动态规划时,没法判断该不该拿走商品的一部分,贪婪算法可以。仅当每个子问题都是离散的,即不依赖于其它子问题时,动态规划才管用。
最长公共子串:
如果两个字母不同,值为0;如果两个字母相同,值为左上角邻居的值加1。
连续的。
最长公共子序列:
如果两个字母不同,就选择上方和左方邻居中较大的那个;如果两个字母相同,就将当前单元格的值设置为左上方单元格的值加1。
不一定连续。
费曼算法:
(1) 将问题写下来。
(2) 好好思考。
(3) 将答案写下来。