【我的第一本算法书】笔记

此笔记仅为我本人学习过程中记录的个人理解,欢迎交流。

如何选择算法

我们最为重视的是算法的运行时间,即从输入数据到输出结果的过程所花费的时间

时间复杂度是一个可以描述算法运行时间的函数,常用大O符号来表述

数据结构

数据存储于计算机内存中,决定了数据顺序和位置关系的就是“数据结构”

将数据存储于内存时,根据使用目的选择合适的数据结构,提高内存利用率

链表

——链表是数据结构之一,其中数据呈线性排列,链表中,数据的添加和删除都较方便,但访问较费时间

链表中的数据量记成N,访问数据时,需从链表头部开始查找(线性查找)。添加数据只需要两个指针,耗费时间与N无关。如果已到达数据位置,添加数据所需时间为O(1),删除数据时间相同。

扩展:可以在链表尾部使用指针,并让它指向链表头部数据,将链表变成环形,形成“循环链表”也叫“环形链表”,没有头尾概念,想保存数量固定的最新数据时通常使用这种链表。

上述链表里每个数据都只有一个链表,但可以设定为两个,并让它们指向前后数据,此为“双向链表”,双向查询数据,十分方便。缺点:指针数的增加导致存储空间需求增加;添加和删除数据时要改变更多指针方向

数组

——也是数据呈线性排列的一种数据结构,访问数据十分简单,而添加和删除数据耗功夫。

访问数据时使用的是随机访问,运行时间仅为恒定的O(1),若想在头部添加数据,需要O(n)时间,删除同理

补充:链表和数组中,数据都是线性排列。链表中添加和删除数据操作简便,访问较复杂。数组相反。

——也是一种呈线性排列的数据结构。在这种结构中只能访问最新添加的数据,栈像一摞书,读取数据都是从最新数据开时。

添加数据叫作“入栈(push)”,取出叫“出栈(pop)”

栈。最后添加的数据最先取出,即“后进先出”的结构,称为Last In First Out

在只需访问最新数据时,使用栈最方便。

队列

——数据也是线性排列,与栈相似,但添加和删除数据是分别在两端进行的,

“入队”,“出队”

最先进去的数据最先被取来,“先进先出”

栈中,数据的添加和删除都在同一端进行,队列中,分别是在两端进行。队列也不能直接访问位于中间的数据,必须通过出队操作将目标变为首位才能访问。

哈希表

哈希表存储的是由key(键)和value(值)组成的数据。Tip:一般把键当成数据的标识符,把值当成数据的内容。

线性查找,数据量越多,耗费时间越长。

mod运算(求余运算) 7 mod 3 =1

在哈希表中,先准备好数组,计算出要存储数据的哈希值,哈希值 求余 数组长度,对应数据存储位置,存储数据过程中发生冲突,可利用链表在已有数据的后面插入新数据来解决冲突。“链地址法”

除外,还有几种解决冲突的方法,应用较广泛的是“开放地址法”——指当冲突发生时,立刻计算出一个候补地址并将数据存进去,,一直到有空地址为止。

因哈希表在数据存储上的灵活性和数据查询上的高效性,编程语言的关联数组等会常常运用它。

堆是一种图的树形结构,被用于实现“优先队列”—可以自由添加数据,但取出数据要从最小值开始按序取出,堆的树形结构中,各顶点被称为“结点”,结点排列顺序为从上到下,同一行里为从左到右。存储规则:子结点必定大于父结点

堆中最顶端的数据始终最小,无论数据量多少,时间复杂度都为O(1),取出数据后需要将最后的数据移到最顶端作为父结点,再与子结点比较,往下移,取出数据需要的运行时间和树的高度成正比。

应用示例:如需频繁地从管理的数据中取出最小值,使用堆操作很方便。

二叉查找数

(又叫二叉搜索树或二叉排序树),采用图的树形结构

每个结点最多有两个子结点,性质:1,每个结点的值均大于其左子树上任意一个结点的值。2,每个结点的值均小于其右子树上任意一个结点的值。得出结论:二叉查找树的最小结点要从顶端开始,往其左下末端寻找。反之,往右末端寻找。

可以把二叉查找树当作是二分查找算法思想的树形结构体现,“平衡二叉查找树”可以修正形状不均衡的树,让其始终保持均衡形态,提高查找效率。

排序

|将数字按从小到大的顺序排列

冒泡排序

重复“从序列右边开始比较相邻两个数字的大小,再根据结果交换两个数字的位置”,在过程中,数字像泡泡一样从右往左浮到序列顶端。

选择排序

“从待排序的数据中寻找最小值,将其与序列最左边的数字进行交换”,在序列中寻找最小值使用的是线性查找。

#冒泡排序和选择排序总的比较次数相同,都约为N²/2,时间复杂度也一样为O(n²)

插入排序

从序列左端开始依次对数据进行排序的算法,排序过程中,左侧数据陆续归位,右侧留下的为未排序的数据。插入排序的思路是从右边区域取出一个数据,将它插入左侧区域内合适的位置上。

插入排序就是将取出的数据与左边数字进行比较再插入,时间复杂度一样为O(n²)

堆排序

特点是利用了数据结构中的堆。用降序构建堆来存储数据,排序时再把数据拿出来。从降序排列中取出数据时会从最大数据开始取,所以将取出的数据反序输出,排序就完成了。

堆排序的时间复杂度为O(nlogn),时间短,但实现起来较困难

归并排序

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

归并排序中,分割序列所花费时间不算在运行时间内,在合并两个已排好序的子序列时,只需重复比较首位数据大小,完成一行归并所需时间取决于这一行的数据量,时间复杂度与堆排序相同,为O(nlogn)

快速排序

——算法首先会在序列中随机选择一个基准值,然后将除了基准值以外的数分为“比基准值小的数”和“比基准值大的数”

【比基准值小的数】 基准值 【比基准值大的数】

对两个【】中数据进行排序后,整体的排序就完成了,对【】里数据进行排序时同样使用快速排序

快速排序法是一种“分治法”,“递归”的一种方法

分割子序列时需要选择基准值,若两个子序列刚好为原本的一半,那么快速序列的运行时间与归并排序一样,为O(nlogn),将序列对半分割以2为底logN次之后,子序列只剩一个数据,此时排序就完成了,那么根据基准值分割会有以2为底logN行,每行运行所需时间为O(n),整体时间复杂度为O(nlogn);若运气不好,每次选最小值作为基准值,递归执行N行,运行时间成了O(n²),相当于每次选出最小值移到最左边,这操作和选择排序一样;此外,若数据中每个数字被选为基准值概率都相等,所需平均时间为O(nlogn)

数组的查找

线性查找

在数组中从头开始依次往下查找

若数据量为n,线性查找的时间复杂度为O(n)

二分查找

通过比较数组中间的数据与目标数据的大小,可得知目标数据在左边还是右边,比较一次,可把查找范围缩小一半。

二分查找利用已排好序的数组,每一次查找都可将查找范围减半。时间复杂度为O(logn)

二分查找的时间复杂度与线性查找相比速度上得到了指数倍提高,(x=logn,则n=2的x方),但是,二分查找必须建立在数据已经排好序的基础上才能使用,因为添加数据时必须找到合适的位置,这需要额外耗费维护数组时间,而线性查找时,数据可以是无序的,少了维护时间。

综上,可根据查找和添加两个操作哪个更为频繁来决定。

图的搜索

——由顶点(也叫结点)和连接每对顶点的边所构成的图形就是图

#图可以表现社会中的各种关系

加权图

由顶点和边构成的图,还可以给边上加一个值,这个值叫边的“权重”或“权”,此为加权图。没权的边只能表示两个顶点的连接状态,有权的边表示顶点之间的“连接程度”。程度,根据图内容的不同,表示意思也不尽相同

有向图

给边加箭头表示方向,有向图也可以加权重

#只要能用图来表示这些关系,就可以用解决图问题的算法来解决这些看似不一样的问题

——

图的搜索算法,和可以解决图的基本问题——最短路径问题的算法

图的搜索算法可分为“广度优先搜索”和“深度优先搜索”

广度优先搜索

特征是从起点开始,由近及远进行广泛搜索,因此目标顶点离起点越近,搜索结束得就越快

从起点开始顺着边搜索,从起点到达的点叫候补点,从候补点中选出一个顶点,优先选择最早成为候补的那个顶点,若同时多个顶点成为候补,则随意选一个,往下。重复操作直到达到终点或所有顶点被历遍为止。

#闭环图,起点终点是同一个顶点,没有闭环的图叫树

深度优先搜索

深度优先搜索和广度优先搜索一样,都是对图进行搜索的算法,目的也都是从起点开始搜索直到到达指定顶点(终点),深度优先搜索会沿着一条路径不断往下搜索直到不能再继续为止,然后再折返,开始搜索下一条候补路径

#广度优先搜索选择的是最早成为候补的顶点,深度优先搜索选择的是最新成为候补的顶点。

贝尔曼-福特算法

——一种在图中求解最短路径问题的算法

最短路径问题就是在加权图指定了起点和终点的前提下,寻找从起点到终点的路径中权重总和最小的那条路径

计算结果小于顶点得值,就更新顶点的权重。

图的顶点数设为 n,边数设为m,整体的时间复杂度就是O(nm)

计算最短路径时,边的权重代表的通常都是时间,距离或者路费等,因此基本都是非负数,但,即便权重为负,贝尔曼-福特算法也可正常运行

如果在一个闭环中边的权重总和是负数,只要不断遍历这个闭环,路径的权重就可以不断缩小,也就是说根本不存在最短路径。直接认定为“不存在最短路径”

狄克斯特拉算法

——也是求解最短问题的算法,使用它可以求得从起点到终点的路径中权重总和最小的那条路径

计算各候补顶点的权重,“目前所在顶点的权重+目前所在顶点到候补顶点的权重”。若计算结果小于候补顶点的值,更新。

比起所有边都要被重复计算的贝尔曼-福特算法,狄克斯特拉算法多了一步选择顶点的操作

图的顶点数设为n,边数设为m,该算法时间复杂度为O(n²),若对数据结构进行优化,时间复杂度就为O(m+nlogn)

#贝尔曼-福特算法和狄克斯特拉算法都是在向图中求解最短路径,但在含有负数权重时,狄克斯特拉算法可能无得出正确答案。

#若闭环中有负数权重,就不存在最短路径。贝尔曼-福特算法可直接认定不存在最短路径,狄克斯特拉算法中,即便不存在,也会算出一个错误的最短路径

A*算法

A*(A-Star)算法是一种在图中求解最短路径问题的算法,由狄克斯特拉算法发展而来;A*会预先估算一个值,并利用这个值来省去一些无用的计算

当知道各个顶点到终点的大致距离时,我们可以使用A*算法

安全和算法

传输数据时可能会发生的主要四个问题

窃听,假冒,篡改,事后否认

解决上述对应问题的安全技术

加密;“消息认证码”或“数字签名”;“消息认证码”或“数字签名”;“数字签名”(预防)

“数字签名”技术存在“无法确认公开密钥的制作者”这一问题,可以使用“数字证书解决这一问题

加密

加密后数据被称为“密文”,密文恢复原本数据的操作叫“解密”

哈希函数

哈希函数可以把给定数据转换为固定长度的无规律数值,哈希值多用十六进制表示

特征:输出的哈希值数据长度不变;如果输入数据相同,哈希值也必定相同;即使输入数据相似,哪怕一比特差别,哈希值也有很大差异;即使输入的两个数据完全不同,也可能出现哈希值相同,“哈希冲突”;不可能从哈希值反向推断出原本数据;求哈希值的计算相对容易

如果输入数据相同,哈希值也必定相同——在使用同一个算法的前提下得出的结论。

共享密钥加密

加密和解密都使用相同密钥的“共享密钥加密”和分别使用不同密钥的“公开密钥加密”

共享密钥加密使用的密钥相同,也叫“对称加密”,但密文可能被窃听的同时,密钥也会被窃听。

公开密钥加密

—加密和解密使用不同密钥的一种加密方法,“非对称加密”。公开密钥存在公开密钥可靠性的问题。中途替换公开密钥来窃听数据的攻击方法,叫“中间人攻击”。出现问题的原因就是无法判断公开密钥是否来自对方,要解决这个问题,引入“数字证书”

#公开密钥加密的加密和解密都比较耗时,不适用于连续发送零碎的数据。解决这个问题,要用到“混合加密”

想找到实现公开密钥加密的算法不容易,需满足以下条件

1 可以使用某个数值对数据进行加密

2 使用另一个数值对加密数据进行计算就可以让数据恢复原样

3 无法从一种密钥推算出另一种密钥

所以RSA等可以实现公开密钥加密的算法的提出对当今互联网社会的安全有重要意义

混合加密

共享密钥加密存在无法安全传输密钥的密钥分配问题,公开密钥加密存在加密解密速度较慢问题。结合两种方法互补的方法就是混合加密

A准备通过互联网向B发送数据,A使用共享密钥加密,与此同时,B要生成公开密钥和私有密钥,将公开密钥发送给A。A将共享密钥加密中需要用到的密钥进行加密,发送给B。B使用私有密钥对密钥进行解密,安全获得了共享密钥有中使用的密钥。接下来,A将加密好的数据发送给B,B安全获得了原本数据

混合加密在安全性和处理速度上都有优势,为网络提供通信安全的SSL协议也应用了混合加密方法

迪菲-赫尔曼密钥交换

—是一种可以在通信双方之间安全交换密钥的方法,将双方共有的秘密数值隐藏在公开数值相关的运算中,来实现双方之间密钥的安全交换

通信双方仅通过交换一些公开信息就可以实现密钥交换,“迪菲-赫尔曼密钥协议”

消息认证码

—可以实现“认证”和“检测篡改”两个功能;消息认证码是预防密文内容在传输过程中可能被篡改,解密过后内容会发生变化,防止误会的机制

加密仅仅是一个数值计算和处理的过程,所以即使密文被篡改,也能够进行解密的相关计算

HMAC是应用最广泛的消息认证码,消息认证码的缺点是,双方都可以对消息进行加密且算出MAC,无法证明消息是谁生成的;使用MAC时,生成一方和检测一方持有同样的密钥,无法确定MAC是谁生成的

数字签名

不仅可以实现消息认证码的认证和检测篡改功能,还可以预防事后否认的问题的发生,数字签名是只有发信人才可以生成。数字签名的生成使用的是公开密钥加密,但是,数字签名与公开密钥加密有区别的是,发送信息,公开密钥和私有密钥都由发送方提供。

公开密钥加密中,用公钥加密数据可以用私钥还原,而数字签名利用的是用私有密钥加密的数据,用公钥还原性质。用发送方的公钥解密的密文,必定是A生成的,利用此结论确认消息发送者是否是发送方,消息是否被篡改。接收方只有公开密钥,无法生存A的签名,预防了“事后否认”这一问题。

#公开密钥加密的加密解密都耗时,因此为节约时间,不对消息加密,对消息求得的哈希值加密,将其作为签名来使用。

数字签名可以实现“认证”,“检测篡改”和“预防事后否认”三个功能,但有缺陷:使用数字签名后接收方B会相信发送方是A,但实际也有可能是X冒充A。

根本原因在于使用公开密钥加密无法确定公钥制作者是谁,收到的公开密钥上也没有任何制作者消息,公钥有可能是某人冒充A做的。

数字证书

“公开密钥加密”和“数字签名”无法保证公开密钥确实来自信息发送者,所以公开密钥被第三者恶意替换,接收方也不会注意到。数字证书能保证公开密钥的正确性

个人之间交付公开密钥用的是数字证书,网站间用的“服务器证书”,同样由认证中心发行。

数字证书是通过认证中心来担保公开密钥的制作者,“公钥基础设施(PKI)”

6-聚类

—就是在输入为多个数据时,将“相似”数据分为一组,“簇”。

如何定义相似

数据类型不同,定义标准也不同;具体说,就是对两个数据之间的“差距”进行定义。

k-means算法

——聚类算法的一种,可以根据事先给定的簇的式里进行聚类

k-means算法中,随着操作的不断重复,中心点的位置必定会在某处收敛

簇的数量即使相同,只要随机设置的中心点最初位置不同,聚类的结果也会产生变化

#除K算法外,“层次聚类算法”较有名,它不需要事先设定簇的数量

——一开始每个数据自成一类,然后重复执行“将距离最近的两个簇合并为一”的操作n-1次,执行n-1次后,所有数据被分到一个簇中。

7-其他算法

欧几里得算法

——(又称辗转相除法)用于计算两个数的最大公约数

首先对两个数进行mod运算,再用除数(较小的数)和余数进行Mod运行,重复操作,直到mod运算结果为0,余数为0时,最后一次运算中的除数,即为最初两数的最大公约数。

#欧几里得算法,只需重复做除法便能求得最大公约数,优势在于即使两个数字再大,按步骤也能高效求得两者最大公约数

素性测试

——判断一个自然数是否为素数的测试;素数只能被1和其本身整除,且大于1的自然数。目前在加密技术中被广泛应用的RSA算法就会用到大素数

若需判断的数非常大,可以用“费马测试”来解决,被称为概率性素性测试。它判断的是"某个数时素数的概率大不大"

p =素数,n<p,n的P次方 mod P =n(费马小定理)

#确认N和余数一致的次数越多,需判断的数确实为素数的可能性越大。若P是素数,那么所有比P小的数都满足N的P次方 mod P = N 这个条件,但反之,即使所有N都满足条件,P也有可能不是素数,561可表示为3X11X17,所以561是合数,不是素数;这些合数叫卡迈克尔数也叫“绝对伪素数”

网页排名

——(PageRank,也叫佩奇排名),是一种在搜索网页时对搜索结果进行排序的算法。

网页排名就是利用网页之间的链接结构计算出网页价值的算法

链接结构为树状,最顶端的页面权重最高,重要性也就越高,价值越大;环状链接结构,在计算各个网页权重的操作会无限循环下去,导致环内网页权重不断增长。此时,使用“随机游走模型”解决此问题。

#从利用网页链接结构计算出网页价值这种思路看,还是从链接形成环状时也能计算,网页排名都是一个划时代的算法

汉诺塔

——一种移动圆盘的游戏,同时也是一个简单易懂的递归算法应用示例

想移动N个圆盘,只需要按照n-1个圆盘的方法移动即可,而要移动n-1个,即可按照N-2时的移动,追朔回去,就会回到移动1个圆盘时的操作方法

在算法描述中调用算法自身的方法就叫做“递归”,递归被运动到各种算法中,统称为“递归算法”。T(n)=2的N次方-1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值