算法图解 -- 书评 - Go语言中文社区

算法图解 -- 书评


算法图解

Grokking Algorithms

作者Aditya Bhargava,美国人。

在这里插入图片描述

这本书的定位正如封面副标题所言–像小说一样有趣的算法入门书,作为一本入门级的算法书,这本书无疑是成功的,书中穿插着大量的图解(作者自述自己为一个视觉性学习者),内容简单,将一些经典的算法结合图解以生动有趣的方式向读者讲解,使非程序员的读者也能轻松理解(除了动态规划部分本身就是一个难点)。同时,以小说为定位也很真实,本书共181页,丰富的图解、形象的表述、生动的表达使读者很容易被吸引,短短一天内即可吸收完这本书的内容精华。

内容提要

本书示例丰富,图文并茂,以简明易懂的方式阐释了算法,旨在帮助程序员在日常项目中更好地利用算法为软件开发助力。前三章介绍算法基础,包括二分查找、大 O 表示法、两种基本的数据结构以及递归等。余下的篇幅将主要介绍应用广泛的算法,具体内容包括 :面对具体问题时的解决技巧,比如何时采用 贪婪算法或动态规划 ;散列表的应用 ;图算法
;K 最近邻算法。

本书适合所有程序员、计算机专业相关师生以及对算法感兴趣的读者。

第一章 算法简介

主要内容

讲解了二分查找算法,介绍了大O表示法。

O(log n),也叫对数时间,这样的算法包括二分查找。
O(n),也叫线性时间,这样的算法包括简单查找。
O(n * log n),这样的算法包括第4章将介绍的快速排序——一种速度较快的排序算法。  O(n2),这样的算法包括第2章将介绍的选择排序——一种速度较慢的排序算法。
O(n!),这样的算法包括接下来将介绍的旅行商问题的解决方案——一种非常慢的算法。
O(1),常数时间。

小结

二分查找的速度比简单查找快得多。
O(log n)比O(n)快。需要搜索的元素越多,前者比后者就快得越多。
算法运行时间并不以秒为单位。
算法运行时间是从其增速的角度度量的。
算法运行时间用大O表示法表示。

第二章 选择排序

主要内容

介绍数组和链表,分析了两者查找、插入、删除的时间复杂度,说明了两者的适用范围。
讲解了选择排序算法的原理和实现。

需要检查的元素数越来越少
随着排序的进行,每次需要检查的元素数在逐渐减少,最后一次需要检查的元素都只有一 个。既然如此,运行时间怎么还是O(n2)呢?这个问题问得好,这与大O表示法中的常数相关。 第4章将详细解释,这里只简单地说一说。
你说得没错,并非每次都需要检查n个元素。第一次需要检查n个元素,但随后检查的元素 数依次为n - 1, n – 2, …, 2和1。平均每次检查的元素数为1/2 × n,因此运行时间为O(n × 1/2 × n)。 但大O表示法省略诸如1/2这样的常数(有关这方面的完整讨论,请参阅第4章),因此简单地写 作O(n × n)或O(n2)。

小结

计算机内存犹如一大堆抽屉。
需要存储多个元素时,可使用数组或链表。
数组的元素都在一起。
链表的元素是分开的,其中每个元素都存储了下一个元素的地址。
数组的读取速度很快。
链表的插入和删除速度很快。
在同一个数组中,所有元素的类型都必须相同(都为int、double等)。

第三章 递归

主要内容

介绍了递归(递归只是让解决方案更清晰,并没有性能上的优势),注意是基线条件(base case)和递归条件(recursive case),重点是调用栈及递归调用栈。

尾递归

小结

递归指的是调用自己的函数。
每个递归函数都有两个条件:基线条件和递归条件。
栈有两种操作:压入和弹出。
所有函数调用都进入调用栈。
调用栈可能很长,这将占用大量的内存。

第四章 快速排序

主要内容

介绍了分治法,讲解了D&C算法–快速排序算法的原理和实现(基准值 pivot 和分区 partitioning)。比较了快排和归并排序的性能。
解释了大O表示法的平均情况和最糟情况。

提示:编写涉及数组的递归函数时,基线条件通常是数组为空或只包含一个元素。陷入困境时, 请检查基线条件是不是这样的。

小结

D&C将问题逐步分解。使用D&C处理列表时,基线条件很可能是空数组或只包含一个元 素的数组。
实现快速排序时,请随机地选择用作基准值的元素。快速排序的平均运行时间为O(n log n)。
大O表示法中的常量有时候事关重大,这就是快速排序比合并排序快的原因所在。
比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时,O(log n)的速度比O(n) 快得多。

第五章 散列表

主要内容

本章介绍了散列表和散列函数及其实现原理,解释了填充因子定义及作用。介绍了Python的散列表的应用–字典 dict()。
介绍了散列表的应用范围:查找、防止重复、缓存。

你可能根本不需要自己去实现散列表,任一优秀的语言都提供了散列表实现。Python提供的 散列表实现为字典,你可使用函数dict来创建散列表。

经验规则: 一旦填装因子大于0.7,就调整散列表的长度。

小结

你几乎根本不用自己去实现散列表,因为你使用的编程语言提供了散列表实现。你可使用
Python提供的散列表,并假定能够获得平均情况下的性能:常量时间。 散列表是一种功能强大的数据结构,其操作速度快,还能让你以不同的方式建立数据模型。
你可能很快会发现自己经常在使用它。
你可以结合散列函数和数组来创建散列表。
冲突很糟糕,你应使用可以最大限度减少冲突的散列函数。
散列表的查找、插入和删除速度都非常快。
散列表适合用于模拟映射关系。
一旦填装因子超过0.7,就该调整散列表的长度。
散列表可用于缓存数据(例如,在Web服务器上)。
散列表非常适合用于防止重复。

第六章 广度优先搜索

主要内容

简介了图(有向图、无向图),讲解了BFS实现算法(运行时间O(V+E))。介绍了拓扑排序。

广度优先搜索是一种用于图的查找算法,可帮助回答两类问题。
第一类问题:从节点A出发,有前往节点B的路径吗?
第二类问题:从节点A出发,前往节点B的哪条路径最短?

小结

广度优先搜索指出是否有从A到B的路径。
如果有,广度优先搜索将找出最短路径。
面临类似于寻找最短路径的问题时,可尝试使用图来建立模型,再使用广度优先搜索来 解决问题。
有向图中的边为箭头,箭头的方向指定了关系的方向,例如,rama→adit表示rama欠adit钱。
无向图中的边不带箭头,其中的关系是双向的,例如,ross - rachel表示“ross与rachel约会,而rachel也与ross约会”。
队列是先进先出(FIFO)的。
栈是后进先出(LIFO)的。
你需要按加入顺序检查搜索列表中的人,否则找到的就不是最短路径,因此搜索列表必 须是队列。
对于检查过的人,务必不要再去检查,否则可能导致无限循环。

第七章 狄克斯特拉算法

主要内容

讲解了狄克斯特拉算法的实现,适用范围为有向无环图DAG且没有负权边。

狄克斯特拉算法包含4个步骤。
(1) 找出“最便宜”的节点,即可在最短时间内到达的节点。
(2) 更新该节点的邻居的开销,其含义将稍后介绍。
(3) 重复这个过程,直到对图中的每个节点都这样做了。
(4) 计算最终路径。

贝尔曼-福德算法(Bellman-Ford algorithm)

小结

广度优先搜索用于在非加权图中查找最短路径。
狄克斯特拉算法用于在加权图中查找最短路径。
仅当权重为正时狄克斯特拉算法才管用。
如果图中包含负权边,请使用贝尔曼-福德算法。

第八章 贪婪算法

主要内容

讲解了如何用贪婪算法解决教室调度问题、背包问题、集合覆盖问题。介绍了NP完全问题,详解了旅行商问题。给出了识别NP完全问题的方法。

贪婪算法很简单:每步都采取最有的做法。用专业术语说,就是你每步都选择局部最优解,最终得到的就是全局最优解。

NP完全问题的简单定义是,以难解著称的问题.
没办法判断问题是不是NP完全问题,但还是有一些蛛丝马迹可循的。

  • 元素较少时算法的运行速度非常快,但随着元素数量的增加,速度会变得非常慢。
  • 涉及“所有组合”的问题通常是NP完全问题。
  • 不能将问题分成小问题,必须考虑各种可能的情况。这可能是NP完全问题。
  • 如果问题涉及序列(如旅行商问题中的城市序列)且难以解决,它可能就是NP完全问题。
  • 如果问题涉及集合(如广播台集合)且难以解决,它可能就是NP完全问题。
  • 如果问题可转换为集合覆盖问题或旅行商问题,那它肯定是NP完全问题。

小结

贪婪算法寻找局部最优解,企图以这种方式获得全局最优解。
对于NP完全问题,还没有找到快速解决方案。
面临NP完全问题时,最佳的做法是使用近似算法。
贪婪算法易于实现、运行速度快,是不错的近似算法。

第九章 动态规划

主要内容

通过讲解了如何用动态规划方法解决背包问题说明动态规划问题的基本方法–网格,又讲解了动态规划问题–最长公共子串。
在这里插入图片描述

在这里插入图片描述

费曼算法(Feynman algorithm)
步骤如下:
(1) 将问题写下来。
(2) 好好思考。
(3) 将答案写下来。

小结

需要在给定约束条件下优化某种指标时,动态规划很有用。
问题可分解为离散子问题时,可使用动态规划来解决。
每种动态规划解决方案都涉及网格。
单元格中的值通常就是你要优化的值。
每个单元格都是一个子问题,因此你需要考虑如何将问题分解为子问题。
没有放之四海皆准的计算动态规划解决方案的公式。

第十章 K最近邻算法

主要内容

讲解了KNN算法的原理,引出了机器学习的概念,简单介绍了OCR、创建垃圾邮件过滤器。

毕达哥拉斯公式(欧式距离)
余弦相似度(cosine similarity)
朴素贝叶斯分类器(Naive Bayes classifier)

小结

KNN用于分类和回归,需要考虑最近的邻居。
分类就是编组。
回归就是预测结果(如数字)。
特征抽取意味着将物品(如水果或用户)转换为一系列可比较的数字。
能否挑选合适的特征事关KNN算法的成败。

第十一章 接下来如何做

主要内容

简单提及了10个作者认为打算深入学习者进一步可以选择的学习内容和方向。

  1. 树(B树、红黑树、堆、伸展树);
  2. 反向索引(inverted index);
  3. 傅立叶变换(非常适合用于处理信号);
  4. 并行算法;
  5. MapReduce(映射和归并,映射是将一个数组转换为另一个数组,归并是将一个数组转换为一个元素);
  6. 布隆过滤器(概率型数据结构,使用散 列表时,答案绝对可靠,而使用布隆过滤器时,答案却是很可能是正确的)和HyperLogLog(一种类似于布隆过滤器的算法);
  7. SHA算法(安全散列算法(secure hash algorithm,SHA)函数,一个散列函数,它生成一个散列值——一个较短的字符串);
  8. 局部敏感的散列算法(相反的为Simhash);
  9. Diffie-Hellman密匙交换(如果你对加密感兴趣,先着手研究 Diffie-Hellman算法是不错的选择:它既优雅又不难理解)(Diffie-Hellman算法解决了如下两个问题: 1. 双方无需知道加密算法。他们不必会面协商要使用的加密算法。2. 要破解加密的消息比登天还难。)
  10. 线性规划(作者觉得最好的算法)(Simplex算法)(如果你对最优化感兴趣, 就研究研究线性规划吧!)。

pdf下载

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_38789531/article/details/83190344
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-08 14:30:06
  • 阅读 ( 1185 )
  • 分类:算法

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢