《算法图解》读书笔记 这是一本用来入门数据结构和算法的一本有趣的书
书中有关于这些算法实现的简单python代码,通俗易懂,适合新手阅读!
以下是我阅读后,根据书中的内容和顺序归纳的重要的知识点
-
算法图解
-
1、算法简介
-
算法是一组完成任务的指令
-
二分查找
- 一般来说,对于n个元素的列表,用二分查找最多需要log2n步,而简单查找最多需要n步
- 二分查找的速度比简单查找快得多
- 仅当列表是有序的时候,二分查找才有用
- 一般来说,对于n个元素的列表,用二分查找最多需要log2n步,而简单查找最多需要n步
-
大O表示法O(n)
- 指出了算法的速度有多块/算法运行时间的增速
- 指出了最糟情况下的运行时间
- O(log n) 对数时间---二分查找
- O(n)线性时间---简单查找
- 最多需要猜测的次数与列表的长度相同时,这种称为线性时间
- O(n*log n)---快速排序(一种速度较快的排序算法)
- O(n2)(n的平方)选择排序(一种速度较慢的排序算法)
- O(n!)阶乘时间---旅行商问题的解决方案(一种非常慢的算法)
- 指出了算法的速度有多块/算法运行时间的增速
-
算法的速度
- 算法的速度指的并非时间(因此不以秒为单位),而是操作数的增数
- 说的是随着输入的增加,其运行时间将以什么样的速度增加
- 从增速的角度度量的
- 算法的运行时间用大O表示法表示
- O(log n)比O(n)快,当需要搜索的元素越多时,前者比后者快得越多
-
-
2、选择排序
-
存储元素的两种基本方式
-
数组
- 所有代办事项在内存中是相连的(紧靠在一起的)
- 优点:需要随机地读取元素时,效率很高,可以迅速的找到数组的任何元素---随机访问
- 在同一个数组中,所有元素的类型必须相同(都为int,double等)
- 缺点:数据少时,会浪费内存;数据多时,会内存溢出
- 读取时间:O(1) ; 插入/删除时间:O(n)
-
链表
- 可以存储在内存的任何地方(分开的)
- 链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在一起
- 优点:添加元素时,不需要移动元素,只需将其放入内存,并将其地址存储到前一个元素中
- 缺点:若要查找一个特定元素,需要遍历前n个元素,效率低---顺序访问
- 读取时间O(n);插入/删除时间O(n)
- 可以存储在内存的任何地方(分开的)
-
术语
- 数组的元素带编号,编号从0开始
- O(n2)(n的平方)---排序算法
- 将元素按顺序排列,每次遍历所有元素,依此找出符合条件的元素并排序
-
-
-
3、递归
-
让解决方案更清晰,并没有性能上的优势
- 循环---程序的性能更高 ;递归---程序更容易理解
-
每个递归函数都由两部分组成
- 递归是指调用自己的函数
- 基线条件(base case)
- 函数不再调用自己,避免形成无限循环
- 递归条件(recursive case)
- 函数调用自己
-
栈
- 调用另一个函数时,当前函数暂停并处于未完成的状态
- 用于存储多个函数的变量,称为调用栈
- 所有函数调用都进入调用栈
- 两种操作:压入和弹出
-
递归调用栈
- 在一个函数调用中不能访问另一个的x变量
- 存储详尽的信息可能占用大量的内存
- 解决占用大量内存的方法
- 重新编写代码,转而使用循环
- 使用尾递归
- 并非所有语言都支持尾递归
- 解决占用大量内存的方法
-
-
4、分而治之(D&C)
-
一种著名的递归式问题解决方法
-
D&C解决问题的两个步骤
- (1)找出基线条件,这种条件必须尽可能简单
- (2)不断将问题分解(或者说缩小规模),直到符合基线条件
- 递归条件——缩小问题规模
- 基线条件很可能是空数组或只包含一个元素的数组
-
快速排序
- 基线条件是数组为空或只包含一个元素
- 步骤
- (1)从数组中选择一个元素,这个元素被称为基准值(pivot)
- (2)分区(partitioning):将数组分为两个子数组:小于基准值的元素和大于基准值的元素
- (3)将这两个子数组进行快速排序
- 归纳证明——证明算法行之有效的方式
- 基线条件
- 归纳条件
- 排序速度取决于选择的基准值
- 平均运行时间O(n logn)
- 最糟情况下的运行时间是O(n2)(n的平方)
- 合并排序
- 运行时间O(n logn)
- 若两种算法的大O运行时间不同,常量将无关紧要;若运行时间相同,那么常量将影响很大
- 由于快速查找与合并查找的运行时间都为O(n logn),而快速查找的常量比合并查找小,则快速查找的速度将更快
- 平均运行时间O(n logn)
- 最糟情况和最佳情况
- 最糟情况
- 层数O(n)*每层需要的运行时间O(n)=O(n2)(n的平方)
- 最佳情况/平均情况
- 层数O(log n)*每层需要的运行时间O(n)=O(n logn)
- 最糟情况
-
-
5、散列表
-
也称为散列映射、映射、字典和关联数组
- 结合散列函数和数组来创建散列表
-
散列函数
- 将同样的输入映射到相同的索引
- 将不同的输入映射到不同的索引
- 散列函数知道数组有多大,只返回有效的索引
- 用来确定元素的存储位置
-
应用
- 将散列表用于查找/模拟映射关系
- python中用dict来创建字典,包含键和值
- 将网址映射到IP地址——DNS解析
- A=dict{ }
- python中用dict来创建字典,包含键和值
- 防止重复
- 将散列表用作缓存/记住数据
- eg.Web服务器上
- 将散列表用于查找/模拟映射关系
-
冲突
- 将两个键分配到了相同的位置
- 解决方法:在该位置存储一个链表
- 若散列表存储的链表很长,散列表的速度将急剧下降
- 避免方法
- 较低的填装因子
- 填装因子:散列表包含的元素数/位置总数
- 填装因子大于1时,意味着填装的元素数>数组的位置数
- 需要在列表中添加位置——调整长度
- 填装因子越低,发生冲突的可能性越小,散列表性能越高
- 通常,一旦填装因子>0.7,就调整散列表的长度
- 良好的散列函数
- 让数组中的值呈均匀分布
- 较低的填装因子
-
性能
- 平均情况
- 执行各操作的时间都为O(1)常量时间
- 最糟情况
- 执行各操作的时间都是O(n)
- 平均情况
-
散列表是无序的,因此添加键值对的顺序无关紧要
-
-
6、广度优先搜索
-
用于图的查找算法
- 从A出发,有前往B的路径吗?(且找出的就是最短路径)
- 从A出发,前往B的哪条路径最短?
-
图
- 由节点和边组成
- 邻居:与某节点相连的众多节点
- 有向图
- 边为箭头,箭头的方向指定了关系的方向
- 无向图
- 边不带箭头,其中的关系是双向的
- 由节点和边组成
-
队列
- 一种先进先出( FIFO )的数据结构
- 栈:先进后出( LIFO )的数据结构,常用于函数调用
-
运行时间:O(V+E),V为顶点数,E为边数
- 面临类似于寻找最短路径的问题时,可尝试使用图来建立模型,再使用广度优先搜索来解决问题
- 找到的是段数最少的路径
- 按加入的顺序检查搜索列表中的元素,否则就不是最短路径,因此搜索列表必须是队列
- 对于检查过的元素,务必不要再去检查,否则可能陷入无限循环
-
-
7、狄克斯特拉算法
- 找出最快的路径——即找出有权图的中总权重最小的路径
- 最短路径指的并不一定是物理距离,也可能是让某种度量指标最小
-
步骤
- (1)找“最便宜”的节点,即可在最短时间内到达的节点
- 散列表
- (2)更新该节点的邻居的开销
- 嵌套散列表
- (3)重复以上过程,直到对图中的每个节点都这样做了
- 循环
- (4)计算最终路径
- (1)找“最便宜”的节点,即可在最短时间内到达的节点
-
术语
- 加权图
- 用狄克斯特拉算法
- 若有负权边,就不能用狄克斯特拉算法,可以用 贝尔曼-福德算法
- 用狄克斯特拉算法
- 非加权图
- 用广度优先搜索
- 加权图
- 找出最快的路径——即找出有权图的中总权重最小的路径
-
8、贪婪算法
- 每步都选择局部最优解,最终得到的就是全局最优解
- 贪婪算法易于实现、运行速度快,是不错的近似算法
- 集合类似于列表,不能包含重复的元素
- python中: | 并集 ;& 交集 ;- 差集
-
NP完全问题
- 以难解著称的问题
- 还没有找到快速解决方案
- 最佳的做法是使用近似算法
- 判断是否是NP完全问题
- 涉及“所有组合”的问题通常是NP完全问题
- 不能将问题分成小问题,必须考虑各种可能的情况,可能是NP完全问题
- 涉及序列(如旅行商问题中的城市序列)且难以解决,可能是NP完全问题
- 涉及集合(如广播站集合)且难以解决,可能是NP完全问题
- 若问题可转换为集合覆盖问题或旅行商问题,那么它肯定是NP完全问题
- 以难解著称的问题
- 每步都选择局部最优解,最终得到的就是全局最优解
-
9、动态规划
-
在给定约束条件下优化某种指标时很有用
- 先解决子问题,再逐步解决大问题
-
当且仅当每个子问题都是离散的,即不依赖于其它子问题时,动态规划才管用
- 每种动态规划解决方案都涉及网格
- 单元格中的值通常就是要优化的值
- 将某个指标最大化
- 最长公共子串
- 最长公共子序列
- 将某个指标最大化
- 每个单元格都是一个子问题,因此需要考虑如何将问题分成子问题,然后来找出网格的坐标轴
- 单元格中的值通常就是要优化的值
-
应用
- 生物学家依据最长公共子序列来确定DNA链的相似性,进而判断两种动物或疾病有多相似;寻找多发性硬化症治疗方案
- git diff 等命令,用来指出两个文件的差异
- 编辑距离(levenshtein distance)指出两个字符串的相似程度。用途有:拼写检查,判断用户上传的资料是否是盗版
- Microsoft Word等具有断字功能的应用程序,确定在什么地方断字以确保行长一致
-
没有放之四海皆准的计算动态规划解决方案的公式
-
-
10、K最近邻算法KNN
-
机器学习
- OCR(光学字符识别)
- 拍摄印刷页面的照片,计算机将自动识别出其中的文字
- 识别步骤
- 浏览大量的数字图像,将数字特征提取出来
- 训练(training)
- 遇到新图像时,提取该图像的特征,再找出最近的邻居
- 浏览大量的数字图像,将数字特征提取出来
- 创建垃圾邮件过滤器
- 使用朴素贝叶斯分类器
- OCR(光学字符识别)
-
度量两个对象有多相似
- 距离公式
- 余弦相似度
-
两项基本工作
- 分类
- 分类就是编组
- 回归
- 回归就是预测结果
- 挑选合适的特征很重要
- 特征抽取意味着将物品转换为一系列可比较的数字
- 能否挑选到合适的特征事关KNN算法的成败
- 特征抽取意味着将物品转换为一系列可比较的数字
- 分类
- KNN用于分类和回归,需考虑最近的邻居
-
11、接下来怎么做
-
树
-
二叉查找树
- 查找/插入/删除 的平均运行时间均为O(logn) 查找时最糟的情况下的平均时间复杂度是O(n)
-
数组
- 查找O(logn) 插入/删除O(n)
-
其它平衡的树
- B树
- 数据库常用它来存储数据
- 红黑树
- 堆
- 伸展树
- B树
-
-
反向索引
- 创建搜索引擎:通过一些关键字,返回用户想访问的页面
-
傅里叶变换
- 非常适用于处理信号
- 压缩音乐
- 地震预测
- DNA分析
- 非常适用于处理信号
-
并行算法
- 为提高算法的速度,需要让它们能在多个内核中并行地执行
- 对数组进行排序时,快速排序的并行版本所需的时间是O(n)
- 即使给电脑装备两个内核,算法的速度也不可能提高一倍
- 并行性管理开销
- 将两个数组合并成一个数组,也需要时间
- 负载均衡
- 并行性管理开销
-
分布式算法
-
MapReduce
- 可以用开源工具Apache Hadoop来使用
- 基于两个理念
- 映射(map)函数
- 将一个数组转换为另一个数组
- 归并(reduce)函数
- 将一个数组转换为一个元素
- 映射(map)函数
- 数据集很大,包含数十亿行时,使用MapReduce只需几分钟就可获得查询结果
- 分布式算法适用于在短时间内完成海量的工作
-
-
布隆过滤器和HyperLogLog
-
布隆过滤器
- 一种概率型数据结构,它提供的答案有可能不对,但可能是正确的
- 可能出现错报的情况
- 不可能出现漏报的情况
- 优点在于占用的存储空间很少
- 非常适用于不要求答案绝对准确的情况
- 一种概率型数据结构,它提供的答案有可能不对,但可能是正确的
-
HyperLogLog
- 近似地计算集合中不同的元素数,不能给出准确的答案,但也八九不离十,而占用的内存空间少很多
- 面临海量数据且只要求答案八九不离十时,可考虑使用概率型算法
-
-
SHA算法
- 散列函数用来确定应将这个值放在数组的什么地方
- 查找时间是固定的O(1)
-
另一种散列函数:安全散列算法(SHA)函数
- 给定一个字符串,SHA返回其散列值——一个较短的字符串
- 用于创建散列表的散列函数接受一个字符串,并返回一个数组索引号
- SHA根据字符串生成另一个字符串
- 应用
- 判断两个文件是否相同
- 在超大型文件时很有用
- 计算它们的SHA散列值,再对结果进行比较
- 在超大型文件时很有用
- 检查密码
- SHA被广泛应用于计算密码的散列值,这种散列算法是单向的——可以根据字符串计算出散列值,无法根据散列值推断出原始字符串
- 要使用SSHA算法来计算密码的散列值,用SHA-2和SHA-3
- 判断两个文件是否相同
- SHA实际上是一系列算法:SHA-0、SHA-1、SHA-2和SHA-3
- 最安全的密码散列函数是bcrypt,但没有任何东西是万无一失的
- 重要特征:局部不敏感
- 若修改其中的一个字符,再计算其散列值,结果将截然不同
- 让攻击者无法通过比较散列值是否类似来破解密码
- 若修改其中的一个字符,再计算其散列值,结果将截然不同
-
局部敏感的散列函数
- Simhash
- 对字符串做细微修改,生成的散列值只存在细微的差别
- 能够比较散列值来判断两个字符串的相似程度
- 对字符串做细微修改,生成的散列值只存在细微的差别
- Simhash
- 散列函数用来确定应将这个值放在数组的什么地方
-
Diffie-Hellman密钥交换
- 解决了
- 双方无需知道加密算法,即不必会面协商要使用的加密算法
- 要破解加密的消息比登天还难
-
使用两个密钥
- 公钥
- 公开的,可将其发布到网站上,通过电子邮件发送给朋友,或其它任何方式来发布
- 私钥
- 要发布消息时,用公钥进行加密,加密后的消息只有使用私钥才能解密
- 公钥
- 解决了
-
线性规划
- 在给定约束条件下最大限度地改善指定的指标
- 线性规划使用Simplex算法
- 所有图算法都可使用线性规划来实现
- 在给定约束条件下最大限度地改善指定的指标
-
-
-