1. 数据结构 主要讲了数组、链表、栈、队列、哈希表、堆、二叉查找树 在存储数据的过程中,如果发生冲突,可以利用链表在已有数据的后面插入新数据来解决冲突。这种方法被称为“链地址法”。除了链地址法以外,还有几种解决冲突的方法。其中,应用较为广泛的是“开放地址法”。这种方法是指当冲突发生时,立刻计算出一个候补地址(数组上的位置)并将数据存进去。如果仍然有冲突,便继续计算下一个候补地址,直到有空地址为止。可以通过多次使用哈希函数或“线性探测法”等方法计算候补地址。 堆是一种图的树形结构,被用于实现“优先队列”(priority queues),在堆中存储数据时必须遵守这样一条规则:子结点必定大于父结点。因此,最小值被存储在顶端的根结点中。往堆中添加数据时,为了遵守这条规则,一般会把新数据放在最下面一行靠左的位置。当最下面一行里没有多余空间时,就再往下另起一行,把数据加在这一行的最左端。 二叉查找树(又叫作二叉搜索树或二叉排序树)是一种数据结构,采用了图的树形结构。 二叉查找树有两个性质。第一个是每个结点的值均大于其左子树上任意一个结点的值。 第二个是每个结点的值均小于其右子树上任意一个结点的值。 首先,二叉查找树的最小结点要从顶端开始,往其左下的末端寻找。此处最小值为3。 比较的次数取决于树的高度。所以如果结点数为n,而且树的形状又较为均衡的话,比较大小和移动的次数最多就是log2n。因此,时间复杂度为O(logn)。但是,如果树的形状朝单侧纵向延伸,树就会变得很高,此时时间复杂度也就变成了O(n)。 有很多以二叉查找树为基础扩展的数据结构,比如“平衡二叉查找树”。这种数据结构可以修正形状不均衡的树,让其始终保持均衡形态,以提高查找效率。另外,虽然文中介绍的二叉查找树中一个结点最多有两个子结点,但我们可以把子结点数扩展为m(m为预先设定好的常数)。像这种子结点数可以自由设定,并且形状均衡的树便是B树。 2. 排序 主要讲了冒泡排序、选择排序、插入排序、堆排序、归并排序、快速排序。 在冒泡排序中,第1轮需要比较n-1次,第2轮需要比较n-2次……第n-1轮需要比较1次。因此,总的比较次数为(n-1)+(n-2)+…+1≈n2/2。这个比较次数恒定为该数值,和输入数据的排列顺序无关。不过,交换数字的次数和输入数据的排列顺序有关。假设出现某种极端情况,如输入数据正好以从小到大的顺序排列,那么便不需要任何交换操作;反过来,输入数据要是以从大到小的顺序排列,那么每次比较数字后便都要进行交换。因此,冒泡排序的时间复杂度为O(n2)。 选择排序使用了线性查找来寻找最小值,因此在第1轮中需要比较n-1个数字,第2轮需要比较n-2个数字……到第n-1轮的时候就只需比较1个数字了。因此,总的比较次数与冒泡排序的相同,都是(n-1)+(n-2)+…+1≈n2/2次。每轮中交换数字的次数最多为1次。如果输入数据就是按从小到大的顺序排列的,便不需要进行任何交换。选择排序的时间复杂度也和冒泡排序的一样,都为O(n2)。 插入排序是一种从序列左端开始依次对数据进行排序的算法。在排序过程中,左侧的数据陆续归位,而右侧留下的就是还未被排序的数据。插入排序的思路就是从右侧的未排序区域内取出一个数据,然后将它插入到已排序区域内合适的位置上。 在插入排序中,需要将取出的数据与其左边的数字进行比较。就跟前面讲的步骤一样,如果左边的数字更小,就不需要继续比较,本轮操作到此结束,自然也不需要交换数字的位置。然而,如果取出的数字比左边已归位的数字都要小,就必须不停地比较大小,交换数字,直到它到达整个序列的最左边为止。具体来说,就是第k轮需要比较k-1次。因此,在最糟糕的情况下,第2轮需要操作1次,第3轮操作2次……第n轮操作n-1次,所以时间复杂度和冒泡排序的一样,都为O(n2)。和前面讲的排序算法一样,输入数据按从大到小的顺序排列时就是最糟糕的情况。 堆排序一开始需要将n个数据存进堆里,所需时间为O(nlogn)。排序过程中,堆从空堆的状态开始,逐渐被数据填满。由于堆的高度小于log2n,所以插入1个数据所需要的时间为O(logn)。每轮取出最大的数据并重构堆所需要的时间为O(logn)。由于总共有n轮,所以重构后排序的时间也是O(nlogn)。因此,整体来看堆排序的时间复杂度为O(nlogn)。这样来看,堆排序的运行时间比之前讲到的冒泡排序、选择排序、插入排序的时间O(n2)都要短,但由于要使用堆这个相对复杂的数据结构,所以实现起来也较为困难。 归并排序算法会把序列分成长度相同的两个子序列,当无法继续往下分时(也就是每个子序列中只有一个数据时),就对子序列进行归并。归并指的是把两个排好序的子序列合并成一个有序序列。该操作会一直重复执行,直到所有子序列都归并为一个整体为止。 归并排序中,分割序列所花费的时间不算在运行时间内(可以当作序列本来就是分割好的)。在合并两个已排好序的子序列时,只需重复比较首位数据的大小,然后移动较小的数据,因此只需花费和两个子序列的长度相应的运行时间。也就是说,完成一行归并所需的运行时间取决于这一行的数据量。看一下上面的图便能得知,无论哪一行都是n个数据,所以每行的运行时间都为O(n)。而将长度为n的序列对半分割直到只有一个数据为止时,可以分成log2n行,因此,总共有log2n行。也就是说,总的运行时间为O(nlogn),这与前面讲到的堆排序相同。 快速排序算法首先会在序列中随机选择一个基准值(pivot),然后将除了基准值以外的数分为“比基准值小的数”和“比基准值大的数”这两个类别,再将其排列成以下形式。[比基准值小的数] 基准值 [比基准值大的数]接着,对两个“[ ]”中的数据进行排序之后,整体的排序便完成了。对“[ ]”里面的数据进行排序时同样也会使用快速排序。 分割子序列时需要选择基准值,如果每次选择的基准值都能使得两个子序列的长度为原本的一半,那么快速排序的运行时间和归并排序的一样,都为O(nlogn)。和归并排序类似,将序列对半分割log2n次之后,子序列里便只剩下一个数据,这时子序列的排序也就完成了。 3. 图的搜索 主要讲了广度优先搜索、深度优先搜索、贝尔曼-福特算法、狄克斯特拉算法、A*算法。 图的搜索指的就是从图的某一顶点开始,通过边到达不同的顶点,最终找到目标顶点的过程。根据搜索的顺序不同,图的搜索算法可分为“广度优先搜索”和“深度优先搜索”这两种。 深度优先搜索会沿着一条路径不断往下搜索直到不能再继续为止,然后再折返,开始搜索下一条候补路径。 深度优先搜索的特征为沿着一条路径不断往下,进行深度搜索。虽然广度优先搜索和深度优先搜索在搜索顺序上有很大的差异,但是在操作步骤上却只有一点不同,那就是选择哪一个候补顶点作为下一个顶点的基准不同。广度优先搜索选择的是最早成为候补的顶点,因为顶点离起点越近就越早成为候补,所以会从离起点近的地方开始按顺序搜索;而深度优先搜索选择的则是最新成为候补的顶点,所以会一路往下,沿着新发现的路径不断深入搜索。 将图的顶点数设为n、边数设为m,我们来思考一下贝尔曼-福特算法的时间复杂度是多少。该算法经过n轮更新操作后就会停止,而在每轮更新操作中都需要对各个边进行1次确认,因此1轮更新所花费的时间就是O(m),整体的时间复杂度就是O(nm)。 与前面提到的贝尔曼-福特算法类似,狄克斯特拉(Dijkstra)算法也是求解最短路径问题的算法,使用它可以求得从起点到终点的路径中权重总和最小的那条路径路径。 比起需要对所有的边都重复计算权重和更新权重的贝尔曼-福特算法,狄克斯特拉算法多了一步选择顶点的操作,这使得它在求最短路径上更为高效。将图的顶点数设为n、边数设为m,那么如果事先不进行任何处理,该算法的时间复杂度就是O(n2)。不过,如果对数据结构进行优化,那么时间复杂度就会变为O(m+nlogn)。 A*(A-Star)算法也是一种在图中求解最短路径问题的算法,由狄克斯特拉算法发展而来。狄克斯特拉算法会从离起点近的顶点开始,按顺序求出起点到各个顶点的最短路径。也就是说,一些离终点较远的顶点的最短路径也会被计算出来,但这部分其实是无用的。与之不同,A*就会预先估算一个值,并利用这个值来省去一些无用的计算。 4. 安全算法 哈希函数的算法中具有代表性的是MD5[插图]、SHA-1[插图]和SHA-2等。其中SHA-2是现在应用较为广泛的一个,而MD5和SHA-1存在安全隐患,不推荐使用。 加密数据的方法可以分为两种:加密和解密都使用相同密钥的“共享密钥加密”和分别使用不同密钥的“公开密钥加密”。 实现共享密钥加密的算法有凯撒密码、AES[插图]、DES[插图]、动态口令等,其中AES的应用最为广泛。 公开密钥加密是加密和解密使用不同密钥的一种加密方法。由于使用的密钥不同,所以这种算法也被称为“非对称加密”。加密用的密钥叫作“公开密钥”,解密用的叫作“私有密钥”。 X把密文发送给B,这个密文由B发出的公开密钥PB加密而成,所以B可以用自己的私有密钥SB来解密。从收到密文到解密密文都没发生任何问题,因此B也意识不到数据已经被窃听。这种通过中途替换公开密钥来窃听数据的攻击方法叫作“中间人攻击”(man-in-the-middle attack)。 公开密钥的可靠性会出现问题,就是因为A无法判断收到的公开密钥是否来自B。要想解决这个问题,就要用到之后会讲到的“数字证书”。公开密钥加密还有一个问题,那就是加密和解密都比较耗时,所以这种方法不适用于持续发送零碎数据的情况。要想解决这个问题,就要用到“混合加密”。 共享密钥加密存在无法安全传输密钥的密钥分配问题,公开密钥加密又存在加密解密速度较慢的问题。结合这两种方法以实现互补的一种加密方法就是混合加密。共享密钥加密存在无法安全传输密钥的密钥分配问题,公开密钥加密又存在加密解密速度较慢的问题。结合这两种方法以实现互补的一种加密方法就是混合加密。 消息认证码可以实现“认证”和“检测篡改”这两个功能。 数字签名不仅可以实现消息认证码的认证和检测篡改功能,还可以预防事后否认问题的发生。 通过数字证书,信息的接收者可以确认公开密钥的制作者。数字证书就是像这样通过认证中心来担保公开密钥的制作者。这一系列技术规范被统称为“公钥基础设施”(Public Key Infrastructure, PKI)。 5. 小结 第5章的安全算法很精彩!把各个算法用图描述的这么清楚还是比较难得的,我之前写了一篇相关算法的文章,画了几十张图前后花了一周的时间,费时费力,我是能体会出作者的用心的。
世界图书日时买了几本人工智能(AI)相关的书籍,刚看完《算法图解》与《我的第一本算法书》这两本,前一本是美国人写的,后一本是日本人写的。虽然都是写算法的书,也都是用图说明算法的书,但差异还是蛮大! 算法是什么呢?——计算或解决问题的步骤。算法与程序有些相似,区别在于:程序是以计算机能够理解的编程语言编写而成,可在计算机上运行;算法是以人类能够理解的方式描述的,用于编写程序之前。不过,这个过程中,到哪里为止是算法、从哪里开始是程序,并没有明确的界限。 即使同一个算法,编程语言也许不同,写出来的程序也可能不同;即便使用相同的编程语言,写程序的人不同,编写出的程序也会不同。 算法的运行时间是从输入数据到输出数据结果这个过程所花费的时间。就算使用同样的算法,花费的时间也会根据所用计算机的不同而产生偏差。在这里,使用“步数”来描述运行时间,“1步”就是计算的基本单位。通过测试“计算从开始到结束总共执行了多少步”来求得算法的运行时间。 《我的第一本算法书》 先说明何为算法,与程序的区别; 然后分别介绍了7种数据结构; 最后是排序、查找、搜索、安全算法(哈希函数和7种加密),以及聚类(K-means算法)和其他算法(欧几里得算法、素性测试、网页排名、汉诺塔)的步骤和原理,每步都用一张图解说。的确是以人类能够理解的方式描述! 而且逻辑非常清晰,比如,讲解完离散数学中的图、加权图、有向图后,随后介绍应用这些图的广度优先搜索、深度优先搜索,还有求最短路径的贝尔曼-福特算法(Bellman-Ford)、狄克斯特拉算法(Dijkstra)、A*(A-Star)算法。同样都是求最短路径,还会对比三种算法在不同情境下的优劣势。如果图中含有负数权重,不能使用狄克斯特拉算法:贝尔曼-福特算法可直接认定不存在的最短路径,但在狄克斯特拉算法中,即便不存在最短路径,也会算出一个错误的最短路径。距离估算值越接近当前顶点到终点的实际值,A-Star算法的搜索效率也越高;反之,若距离估算值与实际值相差较大,该算法的效率可能会比狄克斯特拉算法还要低;如果差距再大,甚至可能无法得到正确答案。 此书中没有介绍编程语言,更没有一步一步的编程代码。而《算法图解》第1章就说明书中示例都使用Python编写的一步一步代码,明确了编程语言是Python,而不是C语言、Ruby或其他。而实际上,算法可以用Python,也可以用C语言、Ruby或其他编程语言编写代码。所以,此书名好像改成“用Python编程语言编写算法与图解”更贴切[微笑] (连同另一本关于算法书籍,写了篇字数更多的读后感,于2019年7月5日发布在“N书斋”微信公众号,多谢指正[抱拳])
《我的第一本算法书》和前面那本《数据结构与算法图解》类似,都是浅显易懂的。 这本相比不足的地方是,它只用图解了算法和数据结构,但是缺少代码实现的部分,前面那本有代码实现,所以跟着上机实操一遍理解会更深入一点。 好的地方是它的内容更丰富一点。 1. 数据结构上添加了堆,它类似于完全二叉树的结构,适用于总是需要得到最小值的情况,比如在迪克斯特拉算法最短路径算法中可以使用。 2.算法上,排序添加了堆排序和归并排序,哈希算法也讲的更加详细一点。另外,算法还增加了两个新的范畴,一个是加解密信息安全算法,这个非常推荐!K mean聚类算法是聚类的入门算法。最后,还有几个经典算法问题,欧几里德的辗转相除法费,费马小定理素性测试法,和网络排名算法。汉诺塔问题初步了解了递归和动态规划的思想。 基于这两本书,去leetcode上刷题,发现还有很多需要补充的知识点: 1.双指针法 这往往是能优化数组操作的关键,它的前提是数组是排好序的,然后就可以做求和问题。 它的变形还有快慢指针,在链表中寻找环形链表,倒数第几个节点等等,都很优秀。 2. 哈希结构 这个其实这两本书都提到了,就是它是一种经典的以空间换时间的方法,因为哈希查找的时间复杂度是O(1),所以它能往往能降低1个n的时间复杂度 3. 动态规划思想 本书提到的汉诺塔问题,其实也是动态规划算法,不过,它没有在此算法上扩展。而这个方法是优化算法的经典思想,还包括斐波那契数列,买卖股票的最佳时机,找数列的最大和。总的来讲,动态规划能够用同样的公式展现状态的变化,能极大的简化算法逻辑。 4.其他小技巧 a.链表加一个哨兵岗,能简化处理头节点的逻辑。 b.双指针尾插法 c.巧用栈和队列等数据结构 持续更新……
什么是能力?把晦涩难懂的专业术语变成简单通俗的语言传达给你。这本书就是如此。越来越喜欢阅读这类轻松的书,在学习中体会快乐,在快乐中收获知识! 若上学时有幸遇见这本书,可能会对数据结构和各类算法理解得更加透彻吧!好在现在也不晚。 有趣,值得一读。
#我的第一本算法书#读后分享 首先这是一本深入浅出的书籍,相信即使不是软件出身的伙伴都能看的津津有味,这本书读它有什么作用呢? 第一,你如果对了解如今各个app和网站都在做各类的推荐算法有兴趣,那么去了解算法背后的推荐机制根源,便是不错的入门方法,因为它可能来源于一些基础查询和排序算法,而这里面就涉及到从最基本的数据存储结构,也就是不同的数据结构,如链表、数组、堆栈、图、哈希表、队列、树,他们其实会根据不同场景得到不同应用,效率也不同。其中哈希表让我印象深刻。 哈希表存储的是键值对,大家可以理解为, 如果我们要存储多个类似于“小步-男”,这样的数据,在表中的顺序如果用1-n的方式去存储,那么数据越多,我们查找的时间越长,而这里哈希表的方式,就是把,“小步”进行哈希计算并按要存储数据的长度,取余数,将得到的值作为数组编号,这样后续做查找的时候,其实我们只需要查找“小步”的哈希值,便可快速得到数据,而不用线性查找 第二,网络世界由于开始蓬勃发展,网络安全已经是作为一个安全上网的支撑,因此,你可曾想过,如果你的所有通讯内容,可能被监听,或者你接收的内容被篡改,那么网络对于我们来说是怎样的存在?那这背后是有哪些加密方式在支撑呢,那么这本书会通过比较容易理解的图文告诉你关于对称加密,不对称加密,数字证书这些我们陌生的内容如果帮我们做好加密这一关。(以前学CCNA的时候咋没有发现这么通俗易懂的讲解) 第三,当你慢慢了解了算法在排序和查询方面那巧妙的计算原理,特别是图的概念后,你可能会将它的某些机理联想到生活中的方方面面,比如它采用的计算某个顶点到另一个顶点的最短路径权重方式,解决了复杂图节点的最小路径的计算。 最后,这本书某种意义上,就是完成对一些算法背景的了解,没有方法论可以获得,不过你阅读后,如果你是非开发人员,至少你会慢慢理解为啥有时候,我们在app中查询东西的时候,有些网站很快,有些很慢。很大可能就是,数据结构使用的差异和查询sql可能需要优化。
其实用配套的软件演示,比看这本书好,因为从动画演示中抽取部分图片有时候和文字匹配的不好,存在略微错位。 本书的排序,图搜索和安全三章可以看看,有个直观印象即可。因为没有涉及具体实现,一些描述可能存在困惑,例如那几种图搜索算法,其实需要队列,栈或者堆来存储候选节点,本书不会涉及这些,所以再走完一路选另一路时,新人可能会困惑。 还有一些描述不到位,有误的,看到的就在文中说了,这里不重复了。
算法的出现以及不同算法的演进,是理性逻辑的胜利,更是“灵感”思维的闪耀!想想几十年前没那么多算法的时候,想要快速完成数据的排序该多么的吃力,而现在我们只需要简单的学习就可以掌握很多先进的排序算法,这是无疑是一件幸运和幸福的事情。当我们仔细去品味算法执行细节的时候,不得不感慨很多算法的设计是多么巧妙!或许我们学习算法时,还应该去复盘反思某个算法出现的背景和思维过程,也许这样我们才能真正的理解算法的精髓,让自己在做系统设计的时候能够快速选择合适的算法。 说回这本书,整体来说还是很不错的。学算法,也许一上来就推导公式,一上来就用代码去实现(尤其是通过C/C++这种“高难度”的编程语言)并不是最好的方式,很容易让人望文生畏,想想自己大学的时候学习数据结构和算法多么痛苦啊。而这本书,最大的价值就在于通过画面以及很容易理解的示例来讲解算法的运行过程,可以让人迅速在脑海中建立起常用数据结构和算法的概念。同时这本书还讲解了安全相关的算法,可以对计算机安全领域有很好的入门和理解,这个也得赞一个👍。总之,很适合想快速理解算法的读者,如果想深入学习以及通过编程实现,那还是需要通过其他书籍啦。
作为算法启蒙书来说确实不错,有图片可以更简易直观地了解理论来源,感觉带有简单的算法会更好。如果我在学c语言的时候看过这本书或许不会觉得它那么难了
四色组合,相得益彰
从我懂了到我讲明白了有很长一段路。向往的境界是我深入浅出,事理缓缓入心。我做不到,作者做到了。把算法讲的如此通俗易懂,是我向往的大脑。年龄大了人就懒了,有时候也不想动脑不想思考,就这样苟着,于是刷一些简单的书扫盲科普加深理解,看假如历史是一群喵,半小时漫画,揭秘这些系列。 这本书还有配套演示,改天一定要看一下。
在图书馆也借阅了这本,这书确实可以,形象通俗易懂,不过应该是我知识不够,能力不行,对于里面的一些算法还不能应用到生活里,导致对它的理解还不是那么到位。 算法比代码高级,也比代码更难,就像过去解题一样,想着有没有其他法子活着更高效的法子,这就体现了算法优化思维,里面的网页排名算法是偏向于SEO的,一般人只能是算法应用层面,根本做不到算法开发层面。 对于理工科的人来说,算法需要数学好,英语好,逻辑性好,脑子好,并且具备较强的求知欲和探索欲,不怕麻烦的品质,否则干不好这块。 补充补充基础知识也是好的。
对于算法初学者是很好的读物,配合精彩的插图,简洁的描述,感觉全书脉络很清晰,希望顺着这个思路,可以多介绍些东西。 对于聚类算法,之前了解的比较少,前段时间算法同事分享聚类知识,到现在看到这里更清楚了些,很不错,了解算法第一步。
通俗易懂,对新手很友好
这是一本我看过的最好的算法入门书,通俗易懂,深入浅出,讲解详尽,插图精美生动,内容实用详尽。推荐算法初学者先好好看看这本书,五星推荐。
算法书翻开了好几本,这本是第一本读完的。算不上特别的系统,但是胜在开头的三章数据结构、排序和搜索用图片讲的很清楚,可以说是“一图胜千言”了,强推。 书里对算法的步骤解释的很清楚,只是没有代码实现。推荐在阅读之余可以用自己熟悉的语言来实现一下。我卡在了迪克斯特拉、A*算法这儿。别的书里应该会有更深入的解释。 对编程没兴趣的,这本书可以跳过了。
1. 数据结构 主要讲了数组、链表、栈、队列、哈希表、堆、二叉查找树 在存储数据的过程中,如果发生冲突,可以利用链表在已有数据的后面插入新数据来解决冲突。这种方法被称为“链地址法”。除了链地址法以外,还有几种解决冲突的方法。其中,应用较为广泛的是“开放地址法”。这种方法是指当冲突发生时,立刻计算出一个候补地址(数组上的位置)并将数据存进去。如果仍然有冲突,便继续计算下一个候补地址,直到有空地址为止。可以通过多次使用哈希函数或“线性探测法”等方法计算候补地址。 堆是一种图的树形结构,被用于实现“优先队列”(priority queues),在堆中存储数据时必须遵守这样一条规则:子结点必定大于父结点。因此,最小值被存储在顶端的根结点中。往堆中添加数据时,为了遵守这条规则,一般会把新数据放在最下面一行靠左的位置。当最下面一行里没有多余空间时,就再往下另起一行,把数据加在这一行的最左端。 二叉查找树(又叫作二叉搜索树或二叉排序树)是一种数据结构,采用了图的树形结构。 二叉查找树有两个性质。第一个是每个结点的值均大于其左子树上任意一个结点的值。 第二个是每个结点的值均小于其右子树上任意一个结点的值。 首先,二叉查找树的最小结点要从顶端开始,往其左下的末端寻找。此处最小值为3。 比较的次数取决于树的高度。所以如果结点数为n,而且树的形状又较为均衡的话,比较大小和移动的次数最多就是log2n。因此,时间复杂度为O(logn)。但是,如果树的形状朝单侧纵向延伸,树就会变得很高,此时时间复杂度也就变成了O(n)。 有很多以二叉查找树为基础扩展的数据结构,比如“平衡二叉查找树”。这种数据结构可以修正形状不均衡的树,让其始终保持均衡形态,以提高查找效率。另外,虽然文中介绍的二叉查找树中一个结点最多有两个子结点,但我们可以把子结点数扩展为m(m为预先设定好的常数)。像这种子结点数可以自由设定,并且形状均衡的树便是B树。 2. 排序 主要讲了冒泡排序、选择排序、插入排序、堆排序、归并排序、快速排序。 在冒泡排序中,第1轮需要比较n-1次,第2轮需要比较n-2次……第n-1轮需要比较1次。因此,总的比较次数为(n-1)+(n-2)+…+1≈n2/2。这个比较次数恒定为该数值,和输入数据的排列顺序无关。不过,交换数字的次数和输入数据的排列顺序有关。假设出现某种极端情况,如输入数据正好以从小到大的顺序排列,那么便不需要任何交换操作;反过来,输入数据要是以从大到小的顺序排列,那么每次比较数字后便都要进行交换。因此,冒泡排序的时间复杂度为O(n2)。 选择排序使用了线性查找来寻找最小值,因此在第1轮中需要比较n-1个数字,第2轮需要比较n-2个数字……到第n-1轮的时候就只需比较1个数字了。因此,总的比较次数与冒泡排序的相同,都是(n-1)+(n-2)+…+1≈n2/2次。每轮中交换数字的次数最多为1次。如果输入数据就是按从小到大的顺序排列的,便不需要进行任何交换。选择排序的时间复杂度也和冒泡排序的一样,都为O(n2)。 插入排序是一种从序列左端开始依次对数据进行排序的算法。在排序过程中,左侧的数据陆续归位,而右侧留下的就是还未被排序的数据。插入排序的思路就是从右侧的未排序区域内取出一个数据,然后将它插入到已排序区域内合适的位置上。 在插入排序中,需要将取出的数据与其左边的数字进行比较。就跟前面讲的步骤一样,如果左边的数字更小,就不需要继续比较,本轮操作到此结束,自然也不需要交换数字的位置。然而,如果取出的数字比左边已归位的数字都要小,就必须不停地比较大小,交换数字,直到它到达整个序列的最左边为止。具体来说,就是第k轮需要比较k-1次。因此,在最糟糕的情况下,第2轮需要操作1次,第3轮操作2次……第n轮操作n-1次,所以时间复杂度和冒泡排序的一样,都为O(n2)。和前面讲的排序算法一样,输入数据按从大到小的顺序排列时就是最糟糕的情况。 堆排序一开始需要将n个数据存进堆里,所需时间为O(nlogn)。排序过程中,堆从空堆的状态开始,逐渐被数据填满。由于堆的高度小于log2n,所以插入1个数据所需要的时间为O(logn)。每轮取出最大的数据并重构堆所需要的时间为O(logn)。由于总共有n轮,所以重构后排序的时间也是O(nlogn)。因此,整体来看堆排序的时间复杂度为O(nlogn)。这样来看,堆排序的运行时间比之前讲到的冒泡排序、选择排序、插入排序的时间O(n2)都要短,但由于要使用堆这个相对复杂的数据结构,所以实现起来也较为困难。 归并排序算法会把序列分成长度相同的两个子序列,当无法继续往下分时(也就是每个子序列中只有一个数据时),就对子序列进行归并。归并指的是把两个排好序的子序列合并成一个有序序列。该操作会一直重复执行,直到所有子序列都归并为一个整体为止。 归并排序中,分割序列所花费的时间不算在运行时间内(可以当作序列本来就是分割好的)。在合并两个已排好序的子序列时,只需重复比较首位数据的大小,然后移动较小的数据,因此只需花费和两个子序列的长度相应的运行时间。也就是说,完成一行归并所需的运行时间取决于这一行的数据量。看一下上面的图便能得知,无论哪一行都是n个数据,所以每行的运行时间都为O(n)。而将长度为n的序列对半分割直到只有一个数据为止时,可以分成log2n行,因此,总共有log2n行。也就是说,总的运行时间为O(nlogn),这与前面讲到的堆排序相同。 快速排序算法首先会在序列中随机选择一个基准值(pivot),然后将除了基准值以外的数分为“比基准值小的数”和“比基准值大的数”这两个类别,再将其排列成以下形式。[比基准值小的数] 基准值 [比基准值大的数]接着,对两个“[ ]”中的数据进行排序之后,整体的排序便完成了。对“[ ]”里面的数据进行排序时同样也会使用快速排序。 分割子序列时需要选择基准值,如果每次选择的基准值都能使得两个子序列的长度为原本的一半,那么快速排序的运行时间和归并排序的一样,都为O(nlogn)。和归并排序类似,将序列对半分割log2n次之后,子序列里便只剩下一个数据,这时子序列的排序也就完成了。 3. 图的搜索 主要讲了广度优先搜索、深度优先搜索、贝尔曼-福特算法、狄克斯特拉算法、A*算法。 图的搜索指的就是从图的某一顶点开始,通过边到达不同的顶点,最终找到目标顶点的过程。根据搜索的顺序不同,图的搜索算法可分为“广度优先搜索”和“深度优先搜索”这两种。 深度优先搜索会沿着一条路径不断往下搜索直到不能再继续为止,然后再折返,开始搜索下一条候补路径。 深度优先搜索的特征为沿着一条路径不断往下,进行深度搜索。虽然广度优先搜索和深度优先搜索在搜索顺序上有很大的差异,但是在操作步骤上却只有一点不同,那就是选择哪一个候补顶点作为下一个顶点的基准不同。广度优先搜索选择的是最早成为候补的顶点,因为顶点离起点越近就越早成为候补,所以会从离起点近的地方开始按顺序搜索;而深度优先搜索选择的则是最新成为候补的顶点,所以会一路往下,沿着新发现的路径不断深入搜索。 将图的顶点数设为n、边数设为m,我们来思考一下贝尔曼-福特算法的时间复杂度是多少。该算法经过n轮更新操作后就会停止,而在每轮更新操作中都需要对各个边进行1次确认,因此1轮更新所花费的时间就是O(m),整体的时间复杂度就是O(nm)。 与前面提到的贝尔曼-福特算法类似,狄克斯特拉(Dijkstra)算法也是求解最短路径问题的算法,使用它可以求得从起点到终点的路径中权重总和最小的那条路径路径。 比起需要对所有的边都重复计算权重和更新权重的贝尔曼-福特算法,狄克斯特拉算法多了一步选择顶点的操作,这使得它在求最短路径上更为高效。将图的顶点数设为n、边数设为m,那么如果事先不进行任何处理,该算法的时间复杂度就是O(n2)。不过,如果对数据结构进行优化,那么时间复杂度就会变为O(m+nlogn)。 A*(A-Star)算法也是一种在图中求解最短路径问题的算法,由狄克斯特拉算法发展而来。狄克斯特拉算法会从离起点近的顶点开始,按顺序求出起点到各个顶点的最短路径。也就是说,一些离终点较远的顶点的最短路径也会被计算出来,但这部分其实是无用的。与之不同,A*就会预先估算一个值,并利用这个值来省去一些无用的计算。 4. 安全算法 哈希函数的算法中具有代表性的是MD5[插图]、SHA-1[插图]和SHA-2等。其中SHA-2是现在应用较为广泛的一个,而MD5和SHA-1存在安全隐患,不推荐使用。 加密数据的方法可以分为两种:加密和解密都使用相同密钥的“共享密钥加密”和分别使用不同密钥的“公开密钥加密”。 实现共享密钥加密的算法有凯撒密码、AES[插图]、DES[插图]、动态口令等,其中AES的应用最为广泛。 公开密钥加密是加密和解密使用不同密钥的一种加密方法。由于使用的密钥不同,所以这种算法也被称为“非对称加密”。加密用的密钥叫作“公开密钥”,解密用的叫作“私有密钥”。 X把密文发送给B,这个密文由B发出的公开密钥PB加密而成,所以B可以用自己的私有密钥SB来解密。从收到密文到解密密文都没发生任何问题,因此B也意识不到数据已经被窃听。这种通过中途替换公开密钥来窃听数据的攻击方法叫作“中间人攻击”(man-in-the-middle attack)。 公开密钥的可靠性会出现问题,就是因为A无法判断收到的公开密钥是否来自B。要想解决这个问题,就要用到之后会讲到的“数字证书”。公开密钥加密还有一个问题,那就是加密和解密都比较耗时,所以这种方法不适用于持续发送零碎数据的情况。要想解决这个问题,就要用到“混合加密”。 共享密钥加密存在无法安全传输密钥的密钥分配问题,公开密钥加密又存在加密解密速度较慢的问题。结合这两种方法以实现互补的一种加密方法就是混合加密。共享密钥加密存在无法安全传输密钥的密钥分配问题,公开密钥加密又存在加密解密速度较慢的问题。结合这两种方法以实现互补的一种加密方法就是混合加密。 消息认证码可以实现“认证”和“检测篡改”这两个功能。 数字签名不仅可以实现消息认证码的认证和检测篡改功能,还可以预防事后否认问题的发生。 通过数字证书,信息的接收者可以确认公开密钥的制作者。数字证书就是像这样通过认证中心来担保公开密钥的制作者。这一系列技术规范被统称为“公钥基础设施”(Public Key Infrastructure, PKI)。 5. 小结 第5章的安全算法很精彩!把各个算法用图描述的这么清楚还是比较难得的,我之前写了一篇相关算法的文章,画了几十张图前后花了一周的时间,费时费力,我是能体会出作者的用心的。
世界图书日时买了几本人工智能(AI)相关的书籍,刚看完《算法图解》与《我的第一本算法书》这两本,前一本是美国人写的,后一本是日本人写的。虽然都是写算法的书,也都是用图说明算法的书,但差异还是蛮大! 算法是什么呢?——计算或解决问题的步骤。算法与程序有些相似,区别在于:程序是以计算机能够理解的编程语言编写而成,可在计算机上运行;算法是以人类能够理解的方式描述的,用于编写程序之前。不过,这个过程中,到哪里为止是算法、从哪里开始是程序,并没有明确的界限。 即使同一个算法,编程语言也许不同,写出来的程序也可能不同;即便使用相同的编程语言,写程序的人不同,编写出的程序也会不同。 算法的运行时间是从输入数据到输出数据结果这个过程所花费的时间。就算使用同样的算法,花费的时间也会根据所用计算机的不同而产生偏差。在这里,使用“步数”来描述运行时间,“1步”就是计算的基本单位。通过测试“计算从开始到结束总共执行了多少步”来求得算法的运行时间。 《我的第一本算法书》 先说明何为算法,与程序的区别; 然后分别介绍了7种数据结构; 最后是排序、查找、搜索、安全算法(哈希函数和7种加密),以及聚类(K-means算法)和其他算法(欧几里得算法、素性测试、网页排名、汉诺塔)的步骤和原理,每步都用一张图解说。的确是以人类能够理解的方式描述! 而且逻辑非常清晰,比如,讲解完离散数学中的图、加权图、有向图后,随后介绍应用这些图的广度优先搜索、深度优先搜索,还有求最短路径的贝尔曼-福特算法(Bellman-Ford)、狄克斯特拉算法(Dijkstra)、A*(A-Star)算法。同样都是求最短路径,还会对比三种算法在不同情境下的优劣势。如果图中含有负数权重,不能使用狄克斯特拉算法:贝尔曼-福特算法可直接认定不存在的最短路径,但在狄克斯特拉算法中,即便不存在最短路径,也会算出一个错误的最短路径。距离估算值越接近当前顶点到终点的实际值,A-Star算法的搜索效率也越高;反之,若距离估算值与实际值相差较大,该算法的效率可能会比狄克斯特拉算法还要低;如果差距再大,甚至可能无法得到正确答案。 此书中没有介绍编程语言,更没有一步一步的编程代码。而《算法图解》第1章就说明书中示例都使用Python编写的一步一步代码,明确了编程语言是Python,而不是C语言、Ruby或其他。而实际上,算法可以用Python,也可以用C语言、Ruby或其他编程语言编写代码。所以,此书名好像改成“用Python编程语言编写算法与图解”更贴切[微笑] (连同另一本关于算法书籍,写了篇字数更多的读后感,于2019年7月5日发布在“N书斋”微信公众号,多谢指正[抱拳])
《我的第一本算法书》和前面那本《数据结构与算法图解》类似,都是浅显易懂的。 这本相比不足的地方是,它只用图解了算法和数据结构,但是缺少代码实现的部分,前面那本有代码实现,所以跟着上机实操一遍理解会更深入一点。 好的地方是它的内容更丰富一点。 1. 数据结构上添加了堆,它类似于完全二叉树的结构,适用于总是需要得到最小值的情况,比如在迪克斯特拉算法最短路径算法中可以使用。 2.算法上,排序添加了堆排序和归并排序,哈希算法也讲的更加详细一点。另外,算法还增加了两个新的范畴,一个是加解密信息安全算法,这个非常推荐!K mean聚类算法是聚类的入门算法。最后,还有几个经典算法问题,欧几里德的辗转相除法费,费马小定理素性测试法,和网络排名算法。汉诺塔问题初步了解了递归和动态规划的思想。 基于这两本书,去leetcode上刷题,发现还有很多需要补充的知识点: 1.双指针法 这往往是能优化数组操作的关键,它的前提是数组是排好序的,然后就可以做求和问题。 它的变形还有快慢指针,在链表中寻找环形链表,倒数第几个节点等等,都很优秀。 2. 哈希结构 这个其实这两本书都提到了,就是它是一种经典的以空间换时间的方法,因为哈希查找的时间复杂度是O(1),所以它能往往能降低1个n的时间复杂度 3. 动态规划思想 本书提到的汉诺塔问题,其实也是动态规划算法,不过,它没有在此算法上扩展。而这个方法是优化算法的经典思想,还包括斐波那契数列,买卖股票的最佳时机,找数列的最大和。总的来讲,动态规划能够用同样的公式展现状态的变化,能极大的简化算法逻辑。 4.其他小技巧 a.链表加一个哨兵岗,能简化处理头节点的逻辑。 b.双指针尾插法 c.巧用栈和队列等数据结构 持续更新……
什么是能力?把晦涩难懂的专业术语变成简单通俗的语言传达给你。这本书就是如此。越来越喜欢阅读这类轻松的书,在学习中体会快乐,在快乐中收获知识! 若上学时有幸遇见这本书,可能会对数据结构和各类算法理解得更加透彻吧!好在现在也不晚。 有趣,值得一读。
#我的第一本算法书#读后分享 首先这是一本深入浅出的书籍,相信即使不是软件出身的伙伴都能看的津津有味,这本书读它有什么作用呢? 第一,你如果对了解如今各个app和网站都在做各类的推荐算法有兴趣,那么去了解算法背后的推荐机制根源,便是不错的入门方法,因为它可能来源于一些基础查询和排序算法,而这里面就涉及到从最基本的数据存储结构,也就是不同的数据结构,如链表、数组、堆栈、图、哈希表、队列、树,他们其实会根据不同场景得到不同应用,效率也不同。其中哈希表让我印象深刻。 哈希表存储的是键值对,大家可以理解为, 如果我们要存储多个类似于“小步-男”,这样的数据,在表中的顺序如果用1-n的方式去存储,那么数据越多,我们查找的时间越长,而这里哈希表的方式,就是把,“小步”进行哈希计算并按要存储数据的长度,取余数,将得到的值作为数组编号,这样后续做查找的时候,其实我们只需要查找“小步”的哈希值,便可快速得到数据,而不用线性查找 第二,网络世界由于开始蓬勃发展,网络安全已经是作为一个安全上网的支撑,因此,你可曾想过,如果你的所有通讯内容,可能被监听,或者你接收的内容被篡改,那么网络对于我们来说是怎样的存在?那这背后是有哪些加密方式在支撑呢,那么这本书会通过比较容易理解的图文告诉你关于对称加密,不对称加密,数字证书这些我们陌生的内容如果帮我们做好加密这一关。(以前学CCNA的时候咋没有发现这么通俗易懂的讲解) 第三,当你慢慢了解了算法在排序和查询方面那巧妙的计算原理,特别是图的概念后,你可能会将它的某些机理联想到生活中的方方面面,比如它采用的计算某个顶点到另一个顶点的最短路径权重方式,解决了复杂图节点的最小路径的计算。 最后,这本书某种意义上,就是完成对一些算法背景的了解,没有方法论可以获得,不过你阅读后,如果你是非开发人员,至少你会慢慢理解为啥有时候,我们在app中查询东西的时候,有些网站很快,有些很慢。很大可能就是,数据结构使用的差异和查询sql可能需要优化。
其实用配套的软件演示,比看这本书好,因为从动画演示中抽取部分图片有时候和文字匹配的不好,存在略微错位。 本书的排序,图搜索和安全三章可以看看,有个直观印象即可。因为没有涉及具体实现,一些描述可能存在困惑,例如那几种图搜索算法,其实需要队列,栈或者堆来存储候选节点,本书不会涉及这些,所以再走完一路选另一路时,新人可能会困惑。 还有一些描述不到位,有误的,看到的就在文中说了,这里不重复了。
算法的出现以及不同算法的演进,是理性逻辑的胜利,更是“灵感”思维的闪耀!想想几十年前没那么多算法的时候,想要快速完成数据的排序该多么的吃力,而现在我们只需要简单的学习就可以掌握很多先进的排序算法,这是无疑是一件幸运和幸福的事情。当我们仔细去品味算法执行细节的时候,不得不感慨很多算法的设计是多么巧妙!或许我们学习算法时,还应该去复盘反思某个算法出现的背景和思维过程,也许这样我们才能真正的理解算法的精髓,让自己在做系统设计的时候能够快速选择合适的算法。 说回这本书,整体来说还是很不错的。学算法,也许一上来就推导公式,一上来就用代码去实现(尤其是通过C/C++这种“高难度”的编程语言)并不是最好的方式,很容易让人望文生畏,想想自己大学的时候学习数据结构和算法多么痛苦啊。而这本书,最大的价值就在于通过画面以及很容易理解的示例来讲解算法的运行过程,可以让人迅速在脑海中建立起常用数据结构和算法的概念。同时这本书还讲解了安全相关的算法,可以对计算机安全领域有很好的入门和理解,这个也得赞一个👍。总之,很适合想快速理解算法的读者,如果想深入学习以及通过编程实现,那还是需要通过其他书籍啦。
作为算法启蒙书来说确实不错,有图片可以更简易直观地了解理论来源,感觉带有简单的算法会更好。如果我在学c语言的时候看过这本书或许不会觉得它那么难了
四色组合,相得益彰
从我懂了到我讲明白了有很长一段路。向往的境界是我深入浅出,事理缓缓入心。我做不到,作者做到了。把算法讲的如此通俗易懂,是我向往的大脑。年龄大了人就懒了,有时候也不想动脑不想思考,就这样苟着,于是刷一些简单的书扫盲科普加深理解,看假如历史是一群喵,半小时漫画,揭秘这些系列。 这本书还有配套演示,改天一定要看一下。
在图书馆也借阅了这本,这书确实可以,形象通俗易懂,不过应该是我知识不够,能力不行,对于里面的一些算法还不能应用到生活里,导致对它的理解还不是那么到位。 算法比代码高级,也比代码更难,就像过去解题一样,想着有没有其他法子活着更高效的法子,这就体现了算法优化思维,里面的网页排名算法是偏向于SEO的,一般人只能是算法应用层面,根本做不到算法开发层面。 对于理工科的人来说,算法需要数学好,英语好,逻辑性好,脑子好,并且具备较强的求知欲和探索欲,不怕麻烦的品质,否则干不好这块。 补充补充基础知识也是好的。
对于算法初学者是很好的读物,配合精彩的插图,简洁的描述,感觉全书脉络很清晰,希望顺着这个思路,可以多介绍些东西。 对于聚类算法,之前了解的比较少,前段时间算法同事分享聚类知识,到现在看到这里更清楚了些,很不错,了解算法第一步。
通俗易懂,对新手很友好
这是一本我看过的最好的算法入门书,通俗易懂,深入浅出,讲解详尽,插图精美生动,内容实用详尽。推荐算法初学者先好好看看这本书,五星推荐。
算法书翻开了好几本,这本是第一本读完的。算不上特别的系统,但是胜在开头的三章数据结构、排序和搜索用图片讲的很清楚,可以说是“一图胜千言”了,强推。 书里对算法的步骤解释的很清楚,只是没有代码实现。推荐在阅读之余可以用自己熟悉的语言来实现一下。我卡在了迪克斯特拉、A*算法这儿。别的书里应该会有更深入的解释。 对编程没兴趣的,这本书可以跳过了。