《我的第一本算法书》基础知识

211 阅读17分钟

时间复杂度:O(n) O:代表忽略重要项以外的内容。

数据结构:
1、链表:数据呈线性排列的数据结构。数据一般是分散存储于内存中的。
分类:

  • 基本链表
  • 循环链表(环形链表)
  • 双向链表:有两个缺点,一是指针数的增加会导致存储空间需求增加;二是添加和删除数据时需要改变更多指针的指向

2、数组:数据呈线性排列的数据结构。数据按顺序存储在内存的连续空间内。

3、栈:线性排列。后进先出(LIFO:Last in First out)。
在栈中,添加和删除数据的操作只能在一端进行,访问数据也只能访问到顶端的数据。

4、队列:线性排列,先进先出(FIFO:First in First out)
数据的添加分别在两端进行,不能直接访问位于中间的数据,必须通过出队操作将目标数据变成首位后才能访问

5、哈希表:

  • 由键(key)和值(value)组成的数据。
  • 从头开始查询的操作叫作“线性查找”。
  • 计算哈希值,计算对应键的哈希值对将要存放的数组的长度进行取余操作(mod运算),得到将放在数组的位置,若位置冲突,可使用链表在已有数据的后面继续存储新的数据

6、堆:

  • 堆是一种图的树形结构,被用于实现“优先队列”。
  • 优先队列是一种数据结构,可以自由添加数据,但取出数据时要从【最小值】按顺序取出。
  • 堆中的每个节点最多有两个子节点。节点的排列顺序为从上到下,同一行里则为从左到右
  • 堆中存储数据的规则:子节点必定大于父节点。因此,最小的值被存储在顶端的跟节点中。
  • 添加数据时,要在堆的最后添加数据,数据会一边比较它与父节点的大小,一边向上移动,直到满足堆的条件为止
  • 取出数据后,要将最后的数据移到最顶端,然后一边比较它与子节点的数据大小,一边向下移动,直到满足堆的条件为止。

7、二叉查找树:

  • 又叫二叉搜索树或二叉排序树,是一种数据结构,采用了图的树形结构。

  • 每个节点最多有两个节点。

  • 两个特性:

    1. 每个节点的值均大于其左子树上任意一个节点的值
    2. 每个节点的值均小于其右子树上任意一个节点的值
  • 二叉查找树的最小节点要从顶端开始,往其左下的末端寻找。

  • 删除数据时:

    1. 若没有子节点,则直接删除
    2. 若只有一个子节点,先删除目标节点,将子节点移动到被删除节点即可
    3. 若有多个子节点,先删除目标节点,递归查找左子树中最大节点移动到被删除节点。
  • 查找数据时:从顶端开始往下查找,和添加数据时一样,小于该节点的值则往左移,大于则往右移

排序:
1、冒泡排序:冒泡排序就是重复“从序列【右边】开始比较相邻两个数字的大小,再根据结果交换两个数字的位置”这一操作的算法。

2、选择排序:选择排序就是重复”从待排序的数据中寻找最小值,将其与序列最左边的数字进行交换“这一操作的算法。在序列中寻找最小值使用的是【线性查找】

3、插入排序:

  • 插入排序是一种从序列左端开始依次对数据进行排序的算法。
  • 在排序的过程中,左侧的数据陆续归位,而右侧留下的就是还未被排序的数据。
  • 插入排序的思路就是从右侧的未排序区域内取出一个数据,然后将它插入到已排序区域内合适的位置上

4、堆排序:

  • 特点是利用了数据结构中的堆。
  • 在堆中存储所有数据,并按降序来构建堆 -》将跟节点(最大值)取出放在数组最右侧 -》重新构建堆 -》重复上述操作直到排序完成

5、归并排序:

  • 归并排序算法会把序列分成长度相同的两个子序列,当无法继续往下分时(也就是每个子序列中只有一个数据时),就对子序列进行归并。
  • 归并指的是把两个排好序的子序列合并成一个有序序列。该操作会一直重复执行,直到所有子序列都归并为一个整体为止。

6、快速排序:

  • 快速排序算法首先会在序列中【随机】选择一个基准值(pivot),然后将除了基准值以外的数分为“比基准值小的数”和“比基准值大的数”这两个类别,再将其排序成下列形式
    * 比基准值小的数】基准值【比基准值大的数】
    接着,对两个“【】”中的数据进行排序之后,整体的排序便完成了。
    对“【】”里面的数据进行排序时同样也会使用快速排序。
    快速排序是一种“分治法”。它将原本的问题分成两个子问题,然后再分别解决这两个问题 不过,解决子问题的时候会再次使用快速排序,甚至在这个快速排序里仍然要使用快速排序。只有在子问题里只剩一个数字时排序才算完成。

数组的查找:
1、线性查找:在数组中从头开始依次往下查找。

2、二分查找:

  • 只能查找已经排好序的数组。
  • 二分查找通过比较数组中间的数据与目标数据的大小,可以得知目标数据是在数组的左边还是右边。重复该操作就可以找到目标或者判定目标不存在。

3、如果对时间复杂度要求有log,一般都需要用到二分查找

图的搜索:

  • 有向图:给边加上箭头,就叫做【有向图】
  • 无向图:边上没有箭头的图便是【无向图】

2、加权图:对称权重和非对称权重

3、图的搜索指的就是从图的某一顶点开始,通过边到达不同的顶点,最终找到目标顶点的过程。
根据搜索的顺序不同,图的搜索算法可分为【广度优先搜索】和【深度优先搜索】

  • 广度优先搜索:假设一开始位于某个顶点(即起点),此时并不知道图的整体结构,而我们的目的是从起点开始顺着边搜索,直到到达指定顶点
    - 在此过程中每走到一个顶点,就会判断一次它是否为终点 - 候补顶点是用“先进先出”的方式管理的 - 特点是从起点开始,由近及远进行广泛的搜索。因此,目标顶点离起点越近,搜索结束得就越快
  • 深度优先搜索:
    • 目的是从起点开始搜索直到到达指定顶点。
    • 深度优先搜索会沿着一条路径不断往下搜索直到不能再继续为止,然后再折返,开始搜索下一条候补路径。
    • 候补顶点是用“后入后出”的方式管理的
  • 广度优先搜索选择的是【最早】成为候补的顶点,因为顶点离起点越近就越早成为候补,所以会从离起点近的地方开始顺序搜索。
  • 深度优先先搜索选择的则是【最新】成为候补的顶点,所以会一路往下,沿着新发现的路径不断深入搜索。

4、贝尔曼-福特算法:

  • 贝尔曼-福特算法是一种在图中求解最短路径问题的算法。
  • 最短路径问题就是在加权图指定了起点和终点的前提下,寻找从起点到终点的路径中权重总和最小的那条路径。
  • 被选中的边两端的权重为“顶点原本的权重+边的权重”,分别计算两个方向的权重,选出最小的。
  • 无向图要两个方向都计算,有向图只要按照边所指向的那个方向来计算就可以了。
  • 将图的顶点数设为n,边数设为m,时间复杂度为:
    该算法经过n轮更新操作后就会停止,而在每轮更新操作中都需要对各个边进行1次确认,因此一轮更新所花费的时间就是O(m),整体的时间复杂度就是O(nm) 遇到对顶点进行n次更新操作后仍能继续更新的情况(例如闭环中边的权重总和是负数),就可以直接认定它为“不存在最短路径”

5、狄克斯特拉算法:求解最短路径问题的算法。

  • 计算各个候补顶点的权重方法:目前所在的顶点的权重 + 目前所在顶点到候补顶点的权重。
  • 从离起点近的顶点开始,按顺序求出起点到各个顶点的最短路径。也就是说,一些离终点较远的顶点的最短路径也会被计算出来,这些其实是无用的。
  • 如果闭环中有负数权重,就不存在最短路径。
  • 有负数权重时 不能使用狄克斯拉算法,因为会计算出一条错误的最短路径,应该使用贝尔曼-福特算法

6、A*算法:A-Star 算法。

  • 会预先估算一个值,并利用这个值来省去一些无用的计算。

  • A* 算法不仅会考虑从起点到候补顶点的距离,还会考虑从当前所在顶点到终点的估算距离。这个估算距离可以自由设定。

  • 若能得到一些准确的启发信息,即各个顶点到终点的大致距离(这个距离不需要准确的值)我们就能使用A* 算法,若无法估算,则不能使用

  • 安全算法
  • 数据传输过程中的问题及解决方法
问题解决方法
窃听加密
假冒消息认证 OR 数字签名
篡改消息认证 OR 数字签名
事后否认数字签名
“数字签名”技术存在“无法确认公开密钥的制作者”这一问题。要想解决这个问题,可以使用“数字证书”技术
  1. 加密基础:加密就是数据经过某种运算后,变成计算机无法理解的数的过程
    加密后的数据被称为“密文”
    把密文恢复健康为原本数据的操作叫作“解密”
    对计算机来说,数据就是一串有意义的数字罗列。密文也是数字罗列,只不过它是计算机无法理解的无规则的数字罗列。
    在加密运算上会用到“密钥”。所以加密就是用密钥对数据进行数值运算
    将数据变成第三者的计算机无法理解的形式,然后再将其恢复成原本数据的一系列操作就是加密技术

  2. 哈希函数:哈希函数可以把给定的数据转换成固定长度的无规律数值。
    哈希值虽是数字,但多用十六进制表示
    哈希函数实际上是在计算机内部进行着某种运算的
    哈希函数的特征:

    • 第一个特征是输出的哈希值数据长度不变(无论输入的多大或多小)
    • 第二个特征是如果输入的数据相同,那么输出的哈希值也必定相同(在使用同一算法的前提下)
    • 第三个特征是即使输入的数据相似,但哪怕它们只有一比特的差别,那么输出的哈希值也会有很大的差异
    • 第四个特征是即使输入的两个数据完全不同,输出的哈希值也有可能是相同的,这种概率很低,被叫做“哈希冲突”
    • 第五个特征是不肯呢个从哈希值反向推算出原本的数据(输入和输出不可逆)
    • 第六个特征是求哈希值的计算相对容易
  3. 共享密钥加密:共享密钥加密是加密和解密都使用相同密钥的一种加密方式。也叫做“对称加密”
    共享密钥加密存在“密钥分配问题”,由于加密解密使用相同密钥,无法确保密钥的传输是否安全,解决这个问题,可以使用“密钥交换协议”和“公开密钥加密”两种方式

  4. 公开密钥加密:

  • 公开密钥加密是加密和解密使用不同密钥的一种加密方式。也叫做“非对称加密” 。加密用的密钥叫作“公开密钥”,解密用的叫作“私有密钥”。
  • 公开密钥存在公开密钥可靠性的问题,就是A无法判断收到的公开密钥是否来自B。要想解决这个问题,要用到“数字证书”
  • 公开密钥还有一个问题就是加密和解密都比较耗时,所以这种方式不适用于持续发送零碎数据的情况。要解决这个问题,需要用到“混合加密”
  • 通过中途替换公开密钥来窃听数据的攻击方式叫作“中间人攻击”。

5、混合加密

  • 共享密钥加密存在无法安全传输密钥的密钥分配问题,公开密钥加密又存在加密解密速度较慢的问题。结合这两种方式以实现互补的一种加密方法就是混合加密
  • 在混合加密中,要用处理速度较快的共享密钥加密对数据进行加密。不过,加密时使用的密钥,则需要用没有密钥分配问题的公开密钥加密进行处理。
  • 混合加密的流程:加入A要发送数据给B
    1. A使用共享密钥加密数据
    2. B使用公开密钥生成公开密钥和私有密钥,将公开密钥发送给A
    3. A使用B发送过来的公开密钥进行加密要解密使用的共享密钥并发送给B
    4. A将共享加密后的数据发送给B
    5. B收到A发送过来的用公开密钥发送过来的共享密钥,使用自己手里的私有密钥进行解密,得到共享密钥,并解密A发送过来的数据

6、迪菲 - 赫尔曼密钥交换

  • 迪菲 - 赫尔曼密钥交换是一种可以在通信双方之间安全交换密钥的方法。这种方法通过将双方共有的【秘密数值】隐藏在公开数值相关的运算中,来实现双方之间密钥的安全交换。
  • 假设有一种方式可以合成两个密钥,这种合成方式有三个特征:(使用这种方式来合成密钥P和密钥S,就会得到由这两个密钥的成分所构成的密钥P-S)
    1. 即使持有密钥P和合成的密钥P-S,也无法把密钥S单独取出来
    2. 不管是怎样合成而来的密钥,都可以把它作为新的元素,继续与别的密钥进行合成。
    3. 密钥的合成结果与合成顺序无关,只与用了哪些密钥有关。
  • 迪菲 - 赫尔曼密钥交换利用了数学难题“离散对数问题”

7、消息认证码

  • 消息认证码可以实现“认证”和“检测篡改”这两个功能。
  • 密文的内容在传输过程中可能会被篡改,这会导致解密后的内容发生变化,从而产生误会。消息认证码就是可以预防这种情况发生的机制。

8、数字签名

  • 数字签名不仅可以实现消息认证码的认证和检测篡改功能,还可以预防事后否认问题的发生
  • 数字签名是只有发信人才能生成的,因此使用它就可以确定谁是消息的发送者了。
  • 数字签名的生成使用的是公开密钥加密
  • 数字签名存在的问题:虽然使用数字签名后B会相信消息的发送者就是A,但实际上也有kennel是X冒充了A。解决方案是使用“数字证书”。

9、数字证书

  • 消息发送者A要先向认证中心(Certification Authority CA)申请发行证书,证明公开密钥确实由自己生成。
  • 消息认证中心会使用自己的私有密钥根据A的资料生成数字签名发送给A,这就是A的数字证书,里面包含A的资料和邮箱。
  • A将作为公开密钥的数字证书发送给了B
  • B收到数字证书,确认证书里的邮件地址是A的地址,向认证中心获取公开密钥
  • B对证书内的签名进行验证,判断它是否为认证中心给出的签名。从证书中取出A的公开密钥

聚类

  • 聚类就是在输入为多个数据时,将“相似”的数据分为一组的操作。一个组就叫做一个“簇”。
  • 如何定义相似:
    1. 定义数据间的差异:根据数据类型不同,定义该数据是否“相似”的标准也不同。具体来说就是要对两个数据之间的“差距”进行定义。
    2. 复合条件的算法:设定什么样的条件取决于进行聚类的目的。

K-means算法:是聚类算法中的一种,它可以根据实现给定的簇的数量进行聚类。

  • 由于k-means 算法需要事先确定好簇的数量,所以设定的数量如果不合理,运行的结果可能会不符合我们的需求。
  • 如果对簇的数量没有明确要求,那么可以事先对数据进行分析,推算出一个合适的数量,或者不断改变簇的数量来实验k-means算法。
  • 即使簇的食量相同,只要随机设置的中心点最初的位置不同,聚类的结果也会产生变化。因此,可以通过改变随机设定的中心点位置来不断尝试k-means算法,再从中选择最合适的聚类结果。

其他算法

  1. 欧几里得算法
  • 又称辗转相除法。用于计算两个数的最大公约数,被称为世界上最古老的算法。
  • 操作流程:
    • 首先用较小的数除较大是数字,求出余数。
    • 接着用除数和余数进行mod预算。
    • 重复上个操作,直到余数为0
    • 余数为0 时,最后一次运算中的除数就是两个数字的最大公约数。
  • 欧几里得算法,只需要重复做除法便能求得最大公约数。
  1. 素行测试:
  • 素行测试是判断一个自然数是否为素数的测试。
  • 素数就是只能被1和自身整除,且大于1的自然数。
  • 费马测试:被称为概率性素行测试,它判断的是”某个数是素数的概率大不大“。
  • 根据是否满足费马小定理来判断一个数是否为素数的方式就是”费马测试“
  • 费马小定理:如果P是素数,那么所有比P小的数n都满足【n mod p = n】这个条件。
  • 单丝反过来,即使所有n都满足条件,P也又可能不是素数。因为在极低概率下会出现所有n都满足条件的合数(非素数的自然数,叫作“卡迈克尔数”,也叫“绝对伪素数”)。
  1. 网页排名
  • 网页排名是一种在搜索网页时对搜索结果进行排序的算法
  • 网页排名就是利用网页之间的链接结构计算出网页价值的算法。
  1. 汉诺塔:汉诺塔是一种移动圆盘的游戏,同时也是一个简单易懂的递归算法应用示例。