《算法图解》《Grokking Algorithm》读书笔记

475 阅读6分钟

本书简介

《算法图解》
author: Aditya Bhargava (美)
译:袁国忠
人民邮电出版社
ISBN  978-7-115-44763-0
英文原版名称:Grokking Algorithms 


路线图:

一、 基础篇(1~3章)

  1. 二分查找 / 大O表示
  2. 数据结构 - 数组/链表
  3. 递归

二、算法介绍

  1. 问题解决思路 (4)-分治法/(8)贪婪算法/(9)动态规划
  2. (5)散列表
  3. 图算法 - (6)广度优先搜索/7狄克斯特拉算法
  4. (10)KNN-K最临近算法
  5.  (11)其余算法 - 10种其余算法

同步更新联系代码库:

github.com/IssaneRen/G…


读书笔记:

一、算法简介:

本章内容很少。

一个二分查找(算法入门),一个大O表示法(也没有讲的很细)。

代码更新在仓库中的binary_search方法里。

别的没啥了。略过?

二、选择排序:

这一章主要内容是学:【数组】/【链表】 的区别。(数据结构相关)

选择排序(排序算法相关)

选择排序其实就是冒泡排序啦。最简单的排序。

即使是不熟悉的python写起来也是非常简单。(具体可以看main.py里面 selection_sort的方法 github.com/IssaneRen/G…

主要要记得对于数组和链表的增删改查耗时的选择。 其实也很简单的。

三、递归:

依旧内容不多,主要讲述的就是递归的思想。

还有方法的调用栈。

这些应该都是平时会记得的知识,复习期间意义不大。

可能只有【基线条件】和【递归条件】这两个名词没什么印象,可能是我学习的时候不叫这些条件吧。

并且,这一章没有太多的可执行的示例代码,大多都是伪代码。

所以,pass~

四、快速排序:

基础内容终于结束了,回忆起上学期间学习算法的排序逻辑了。

这一章主要是从快速排序入手。

第一节主要讲D&C,即分而治之。练习代码还是挺多的,虽然都不难,但是练练手还不错。

第二节主要是将快速排序。也是第一节内容的具体实现。思路就是选择一个基准值,把所有小于它的放在前面,所有大于它的放在后面。

也没有什么理解上的难度,但是对于python的语法糖还是稍微学习了一下。less = [i for i in array[1:] if i <= pivot] 这种 可以直接复制数组中所有符合条件的内容。

哦对,还有【pivot】/ ˈpɪvət / 这个单词,基准值。感觉也会很常用的。

第三节主要是重新认识了一下大O表示法。

主要是和一开始简单的讨论一个维度和现在讨论两个维度(执行步数和每一步的复杂程度 / 最糟时间和平均时间)。

值得注意的是,快速排序的平均时间=最佳时间。

五、散列表:

第五章主要又回到了数据结构。
说散列表可能很多资历老的开发同学也反应不过来,不过只要叫他hash表 哈希表,我猜没人不清楚。。。
所以了解不同的翻译也是会方便不少。

这一章主要两个部分。先是了解散列表/哈希表; 然后是了解其中 实现、冲突和散列函数 等原理。 先是对哈希散列的简单介绍,类似这种简单习题的概念问题。其实应该是只有5.4是符合概念的,这里我也就不多赘述啦。

Image.png (↑这道题应该很简单就能看出是 5.3 才是正确答案吧,不过只要你能看出来这个,这节课的内容也就说明你是完全掌握的)

然后讲述了 【填装因子】【冲突处理】 等概念。

这些概念和哈希表的具体实现相关,不过鉴于本书主要面向入门开发者, 具体底层实现也没有讲,只要你知道哈希表的概念就行啦。(后面的读书笔记我会详细记录的)

六、广度优先搜索:

广度优先搜索又称BFS - Breadth-first search。主要目的就是找出可达路径和最短距离。

虽然这一章看起来主要讲的是其中引申出来的其他概念: 【图】这个数据结构(有向图/无向图) + 拓扑排序  + 【队列】这种数据结构

记得上学期间这些概念都比较复杂,但是粗读了一下这本书的这一章其实还是比较浅显。

首先是简单介绍图的概念。很显然,下面这个就是一个图,如果不深究定义的话其实还是很好理解的。

有点,有线。 

Image.png

至于在代码中如何实现,笔者表示在python中是靠一个 value为数组的散列表实现的。

graph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []

最后,我们讨论一下广度优先搜索的复杂度:

广度优先搜索的运行时间为:

O(人数 + 边数),这通常写作O(V + E),其中V为顶点(vertice)数,E为边数。

七、狄克斯特拉算法:

这也是上学期间的经典算法了,当时学的时候记得老师在这节课讲了好久,所以反而感觉不是很难,毕竟花的课时多自然也就理解了。

虽然不知道如果老师没多花时间的话,这个的难度会不会很难以接受,但是还是建议这节课仔细读完吧。

这一章主要是学 1. 加权图(边有权重,或者说耗时,长度 的图)  2. 狄克斯特拉算法本身  3. 有环图,会让狄克斯特拉算法无效。

先是通过直观的图示展示了狄克斯特拉算法怎么选择最便宜的节点持续推进,

后面告知了【有环图】和【负权图】都会让算法失效。(PS: 对于【负权图】,可以采用Bellman-Ford  algorithm来找到最短路径,后面的复习笔记也会再讲。)

下面就是实战了。我们使用python进行练习,练习的时候会使用两层的HashMap散列表来存储。

比如存储下面这样一个DAG(有向无环图):

Image.png

存放的python代码即为:

# 7.1 构建示例DAG 有向无环图
def generate_dag():
    diagram = {}
    diagram["start"] = {}
    diagram["start"]["A"] = 6
    diagram["start"]["B"] = 2
    diagram["A"] = {}
    diagram["A"]["end"] = 1
    diagram["B"] = {}
    diagram["B"]["A"] = 3
    diagram["B"]["end"] = 5

现在我们针对这个图,测试一下我们编写的下述狄克斯特拉算法:

# 7.5 狄克斯特拉算法 实现
def add_init_nodes(diagram, node, costs):
    for n in diagram[node].keys():
        costs[n] = diagram[node][n]
    for n in diagram.keys():
        if (n not in costs):
            costs[n] = float("inf")

def find_lowest_cost_node(costs, known_nodes):
    min_cost = float("inf")
    lowest_cost_node = None
    for node in costs.keys():
        if (node not in known_nodes and node in costs and costs[node] < min_cost):
            min_cost = costs[node]
            lowest_cost_node = node
    return lowest_cost_node, min_cost

def dijkstra_alg(diagram, start, end):
    # 1. 消耗时间
    costs = {}
    # 2. 父节点
    parents = {}
    parents["start"] = 0
    # 3. 已经遍历过的内容
    known_nodes = []
    add_init_nodes(diagram, start, costs)
    current_node, current_cost = find_lowest_cost_node(costs, known_nodes)
    while current_node != None:
        known_nodes.append(current_node)
        for n in diagram[current_node].keys():
            if ((n in costs and (costs[n] > (current_cost + diagram[current_node][n]))) or n not in costs):
                costs[n] = current_cost + diagram[current_node][n]
                parents[n] = current_node
        current_node, current_cost = find_lowest_cost_node(costs, known_nodes)
    # 退出循环表示完全遍历完成,现在找到路径
    final_cost = float("inf")
    if (end in costs):
        final_cost = costs[end]
    return final_cost

if __name__ == '__main__':
    diagram = generate_dag()
    print(str(dijkstra_alg(diagram, "start", "end")))

这一块其实书上并没有贴出完整的代码,只是拆分出来提供了展示,可能就是希望大家动手实现一下吧。
可以参考上述代码的实现看下。