【Algorithms 4】算法(第4版)学习笔记 16 - 4.2 有向图

前言

本篇主要内容包括:有向图 API有向图搜索拓扑排序 以及 强连通分量

强烈建议在学习本篇之前先行学习或回顾上一篇无向图的内容。

参考目录

  • B站 普林斯顿大学《Algorithms》视频课
    (请自行搜索。主要以该视频课顺序来进行笔记整理,课程讲述的教授本人是该书原版作者之一 Robert Sedgewick。)
  • 微信读书《算法(第4版)》
    (本文主要内容来自《4.2 有向图》)
  • 官方网站
    (有书本配套的内容以及代码)

学习笔记

注1:下面引用内容如无注明出处,均是书中摘录。
注2:所有 demo 演示均为视频 PPT demo 截图。
注3:如果 PPT 截图中没有翻译,会在下面进行汉化翻译,因为内容比较多,本文不再一一说明。

1:介绍

1.1:有向图简介

在这里插入图片描述

书中有向图图解:

在这里插入图片描述

1.2:应用举例

image-20240307083507140

1.3:相关问题

![L13-42DirectedGraphs_11]

问题描述
s → t path从 s 到 t 的路径是否存在?
shortest s → t path从 s 到 t 的最短路径是什么?
directed cycle图中是否存在有向环?
topological sort (拓扑排序)能否将有向图绘制为所有边都指向上的拓扑排序形式?
strong connectivity (强连通性)图中任意两点之间是否存在方向任意的有向路径?
transitive closure (传递闭包)对于哪些顶点 v 和 w,存在从 v 到 w 的有向路径?
PageRank (页面排名算法)网页的重要性是什么?

2:有向图 API

![image-20240307091117855]

2.1:有向图表示

2.1.1:邻接表数组 Adjacency-list

![L13-42DirectedGraphs_15]

2.1.2:Java 实现:邻接表数组

(可参考对比无向图 Graph 实现)

edu.princeton.cs.algs4.Digraph

![image-20240307095043211]

edu.princeton.cs.algs4.Digraph#Digraph

![image-20240307095141935]

edu.princeton.cs.algs4.Digraph#addEdge

![image-20240307095232234]

edu.princeton.cs.algs4.Digraph#adj

![image-20240307095333624]

2.2:实际应用

![image-20240307095644883]

**在实际应用中:**我们采用邻接表的方式来表示图结构。

  • 许多算法都是基于从顶点 v 出发逐个遍历其指向的所有其他顶点。
  • 实际应用中的有向图数据结构往往具有稀疏特性。(虽然顶点数量可能非常庞大,但平均每个顶点所连接的边数相对较少。)

2.3:小结

![image-20240307100306896]

注:边的数组 list of edges 以及 邻接矩阵 adjacency matrix 没有详细说明,可以参考无向图 #2.4 相关内容。

3:有向图搜索

3.1:可达性

![L13-42DirectedGraphs_20]

3.2:深度优先搜索 depth-first search

![L13-42DirectedGraphs_21]

与无向图方法相同:

  • 每一个无向图都可以视为一个有向图(每条边都有两个方向)。
  • 深度优先搜索(DFS)是一个应用于有向图的算法。

3.2.1:demo 演示

![image-20240308083822573]

访问一个顶点 v 时:

  • 将顶点 v 标记为已访问状态。
  • 递归地访问从顶点 v 出发的所有未标记的顶点。

初始状态:

![image-20240308084052618]

同样地,v 代表顶点,marked[] 保存标记状态,edgeTo[] 保存边信息。

访问第一个顶点 0:

![image-20240308084405582]

依次检查 与顶点 0 相邻且由此出发所指向的 顶点:分别是 5、1。

检查顶点 5:

![image-20240308084715158]

检查与顶点 5 相邻且由此出发所指向的顶点:4。

检查顶点 4:

![image-20240308084900765]

检查与顶点 4 相邻且由此出发所指向的顶点:分别是 3、2。

检查顶点 3:

![image-20240308085020730]

检查与顶点 3 相邻且由此出发所指向的顶点:分别是 5、2。

检查顶点 5,已经被标记。

检查顶点 2:

![image-20240308085202510]

检查与顶点 2 相邻且由此出发所指向的顶点:分别是 0、3。

检查顶点 0,已经被标记。

检查顶点 3,已经被标记。

完成顶点 2 的搜索:

![image-20240308085351466]

返回顶点 3,完成顶点 3 的搜索:

![image-20240308085516067]

返回顶点 4 继续搜索:

检查顶点 2,已经被标记。

完成顶点 4 的搜索:

![image-20240308085731886]

返回顶点 5,完成顶点 5 的搜索:

![image-20240308085811186]

返回顶点 0 继续搜索:

搜索顶点 1,没有需要搜索的相邻节点,完成顶点 1 的搜索:

![image-20240308085939969]

完成顶点 0 的搜索:

![image-20240308090119040]

完成了所有顶点 0 可达的顶点搜索:

![image-20240308090233915]

3.2.2:Java 实现

(与无向图完全相同,只是替换了方法名称以及对象类型)

edu.princeton.cs.algs4.DirectedDFS

![image-20240308090856191]

edu.princeton.cs.algs4.DirectedDFS#dfs

![image-20240308090914605]

edu.princeton.cs.algs4.DirectedDFS#marked

![image-20240308090928992]

3.3:可达性应用

3.3.1:程序控制流分析

![image-20240308092044359]

每个程序都可以表示为一个有向图。

  • 顶点(Vertex):表示一组连续执行的指令集(基本块)。
  • 边(Edge):表示程序控制流中的跳转关系。

死代码消除。
寻找并移除不可达(永远不会被执行)的代码。

无限循环检测。
判断程序是否存在无法到达退出点的情况。

3.3.2:标记-清除垃圾回收

![image-20240308092832284]

每种数据结构都可以表示为一个有向图。

  • 顶点(Vertex):表示对象(Object)。
  • 边(Edge):表示引用(Reference)。

根对象(Roots)。
指那些可以直接被程序访问的对象(例如,栈中的对象)。

可达对象(Reachable objects)。
指那些通过程序可以从根对象间接访问到的对象(从某个根开始,沿着一系列指针链进行跟踪)。

![image-20240308093117066]

标记-清除算法(由麦卡锡于 1960 年提出):

  • 标记阶段(Mark):标记所有能够被程序访问到的对象。
  • 清除阶段(Sweep):若对象未被标记,则判定其为垃圾对象(从而将它加入到空闲列表中释放)。

内存成本:
该算法对每个对象需要额外占用 1 个标记位,并且还需要使用深度优先搜索栈(这会增加一定的内存开销)。

标记-清除算法 是学习 JVM 的时候一个很重要的算法。

3.4:小结

![L13-42DirectedGraphs_29]

深度优先搜索(DFS)能够直接解决一些简单的有向图问题。

  • 可达性(判断图中两个顶点间是否存在可达路径。)
  • 路径查找(找到一条从起点到终点的路径。)
  • 拓扑排序(对有向无环图中的顶点进行排序,使得对于任意指向顶点 u 的边,顶点 u 都在顶点 v 之前。)
  • 有向循环检测(判断有向图中是否存在环路。)

深度优先搜索是解决复杂有向图问题的基础方法。

  • 二元可满足性问题求解(在逻辑运算中判断给定的布尔公式是否能通过真值赋值使其结果为真的问题。)
  • 有向欧拉路径(在有向图中寻找一条经过每条边恰好一次的路径。)
  • 强连通分量(识别出有向图中互相可达的所有顶点集合,这些集合内的顶点两两之间都存在可达路径。)

3.5:广度优先搜索 breadth-first search

![L13-42DirectedGraphs_30]

同样的方法也适用于无向图。

  • 每个无向图都可以看作是有向图(每条边都是双向的)。
  • 广度优先搜索(BFS)是一种有向图算法。

**命题:**在有向图中,广度优先搜索(BFS)可以在时间复杂度为 O(E+V) 的时间内,计算出从顶点 s 到其他所有顶点的最短路径(即最少边数的路径)。

3.5.1:demo 演示

![image-20240308112651220]

重复执行以下操作,直到队列为空:

  • 从队列中移除顶点 v。
  • 将从顶点 v 出发的所有未标记的顶点加入队列,并将它们标记为已访问。

初始状态:

![image-20240308112814925]

将顶点 0 添加到队列:

![image-20240308113008940]

queue 代表队列,v 代表顶点,edgeTo[] 保存边信息,distTo[] 保存路径长度信息。

顶点 0 出队:

![image-20240308113155176]

需要依次检查 与顶点 0 相邻且由此出发所指向的 顶点:分别是 2、1。

![image-20240308113313544]

检查顶点 2:

![image-20240308113440038]

顶点 2 没有被标记,添加到队列中。

检查顶点 1:

![image-20240308113515785]

顶点 1 没有被标记,添加到队列中。

完成顶点 0 搜索:

![image-20240308113622358]

顶点 2 出队:

![image-20240308113912866]

依次检查与顶点 2 相邻且由此出发所指向的顶点:4。

![image-20240308114040848]

检查顶点 4:

![image-20240308114137636]

顶点 4 没有被标记,添加到队列中。

完成顶点 2 搜索:

![image-20240308114213642]

顶点 1 出队:

![image-20240308114303032]

依次检查与顶点 1 相邻且由此出发所指向的顶点:2。

检查顶点 2,已经被标记。

完成顶点 1 搜索:

![image-20240308114433307]

顶点 4 出队:

![image-20240308114539272]

依次检查与顶点 4 相邻且由此出发所指向的顶点:3。

![image-20240308114807806]

检查顶点 3:

![image-20240308114855110]

顶点 3 没有被标记,添加到队列中。

完成顶点 4 搜索:

![image-20240308114932405]

顶点 3 出队:

![image-20240308115007828]

依次检查与顶点 3 相邻且由此出发所指向的顶点:分别是 5、2。

![image-20240308115112474]

检查顶点 5:

![image-20240308115323083]

顶点 5 没有被标记,添加到队列中。

检查顶点 2,已经被标记。

完成顶点 3 搜索:

![image-20240308115343655]

顶点 5 出队:

![image-20240308115449300]

依次检查与顶点 5 相邻且由此出发所指向的顶点:0。

![image-20240308115429564]

检查顶点 0,已经被标记。

完成顶点 5 搜索:

![image-20240308115515870]

完成所有搜索:

![image-20240308115548973]

3.5.2:多源最短路径

![L13-42DirectedGraphs_33]

多源最短路径
给定一个有向图和一组起点顶点,找出从该组任意起点顶点到图中其他所有顶点的最短路径。

Q. 如何实现多源最短路径算法?
A. 可以使用广度优先搜索(BFS),但是在初始化时,将所有源顶点都入队列。 (具体做法是首先将所有指定的源顶点放入队列中作为搜索的起点,然后按照广度优先搜索的规则依次遍历图中的其他顶点,逐步计算出从各个源顶点到图中所有其他顶点的最短路径。)

3.6:BFS 应用

3.6.1:网络爬虫

![L13-42DirectedGraphs_34]

**目标:**从某一指定的根网页(例如www.princeton.edu)出发爬取整个网站。

**解决方案:**使用 广度优先搜索(BFS) 算法隐式构建的有向图 进行爬取。

步骤如下:

  1. 将根网页设为源节点 s
  2. 维护一个用于存储待探索网站的 队列(Queue)
  3. 维护一个记录已发现网站的 集合(SET),用于去重。
  4. 从队列中取出下一个网站,并检查其指向的所有链接所对应的网站(前提是这些网站尚未被添加进队列或集合中)。
    • 如果某个链接指向的网站还未被发现,则将其加入队列。
    • 同时将新发现的网站添加到已发现网站的集合中,确保不会重复抓取同一网站。

Q. 为什么不用 DFS 实现?

(利用通义千问总结一下) 网络爬虫的实现通常首选广度优先搜索(BFS)而不是深度优先搜索(DFS)的原因在于以下几个关键因素:

  1. 短路径优先
    • BFS 保证爬虫首先遍历到的是离起始网页最近的网页,因此对于寻找最短路径的情况非常有利。在网络爬虫场景下,如果目的是快速抓取到尽可能接近种子 URL 的网页,BFS 能够找到从起始网页到任意页面的最短跳转路径。
  2. 更全面的初始层级覆盖
    • BFS 逐层遍历,能更均匀地抓取同一层级上的所有链接,这对于初步建立网站的拓扑结构、索引和导航结构的初步构建十分有效,能够迅速获取到一个网站的第一层链接资源。
  3. 避免过深探索
    • DFS 可能会导致爬虫陷入深层次且可能不是特别重要的链接结构中,而忽视了其他重要性较高的较浅层次页面。尤其是在没有合理剪枝策略的情况下,DFS 可能导致爬虫在某一分支上耗费过多资源,而忽略了其他可能更重要的部分。
  4. 减少重复爬取
    • BFS 通过维护一个已经访问过的网页集合(通常是哈希表),能够有效地避免爬虫反复进入同一个网页的多个入口,减少了不必要的重复抓取。
  5. 反映网页的重要性
    • 在某些基于链接流行度的评价体系中,入链数量往往代表了一定程度上的网页重要性。BFS 倾向于先抓取那些有更多的入链(即被更多网页链接到的网页),这在一定程度上符合优先抓取重要网页的需求。

综上所述,网络爬虫采用 BFS 作为基础遍历策略,能够更好地满足实际应用中的需求,如高效地获取范围广泛的链接、平衡地抓取不同层次的内容以及减少不必要开销等。当然,具体实现时根据任务的具体要求,有时候也会结合 DFS 或其他策略来优化爬虫的行为。

3.6.2:Java 实现

![L13-42DirectedGraphs_35]

4:拓扑排序 topological sort

4.1:定义

![L13-42DirectedGraphs_39]

4.2:demo 演示

![image-20240309124401947]

  • 执行深度优先搜索
  • 按逆后序返回顶点

初始状态:

![image-20240309124627112]

访问第一个顶点 0:

![image-20240309124957020]

依次检查相邻顶点:1、2、5。

检查顶点 1:

![image-20240309125144970]

检查相邻顶点:4。

检查顶点 4:

![image-20240309125252832]

顶点 4 没有出度,完成搜索并加入堆栈(后序 postorder):

![image-20240309125515783]

返回顶点 1,完成搜索并加入后序:

![image-20240309125720202]

返回顶点 0 继续搜索。

检查顶点 2:

![image-20240309132239841]

顶点 2 没有出度,完成搜索并加入后序:

![image-20240309132322686]

返回顶点 0 继续搜索。

检查顶点 5:

![image-20240309132404455]

检查顶点 2,已经被标记。

顶点 5 完成搜索并加入后序:

![image-20240309132510999]

完成顶点 0 的搜索并加入后序:

![image-20240309132602881]

按顺序检查其他未标记顶点。

顶点 1、2 已经被标记,检查顶点 3:

![image-20240309132929427]

依次检查相邻顶点:2、4、5、6。

检查顶点 2,已经被标记。

检查顶点 4,已经被标记。

检查顶点 5,已经被标记。

检查顶点 6:

![image-20240309133145225]

检查相邻顶点:0、4。

检查顶点 0,已经被标记。

检查顶点 4,已经被标记。

完成顶点 6 的搜索并加入后序:

![image-20240309133254279]

完成顶点 3 的搜索并加入后序:

![image-20240309133329349]

完成所有顶点搜索。

4.3:Java 实现:DFS 排序

edu.princeton.cs.algs4.DepthFirstOrder

![image-20240309134132609]

edu.princeton.cs.algs4.DepthFirstOrder#dfs

![image-20240309134209810]

edu.princeton.cs.algs4.DepthFirstOrder#reversePost

![image-20240309134249391]

4.4:DAG 拓扑排序证明

![L13-42DirectedGraphs_44]

对应书本命题 F:

![image-20240309134810658]
img
图4.2.11 有向无环图的逆后序是拓扑排序

4.5:有向循环检测

![L13-42DirectedGraphs_45]

**命题:**一个有向图存在拓扑排序当且仅当它不含任何有向环。

证明:

  • 若存在有向环,则无法构造拓扑排序。
  • 若不存在有向环,则基于深度优先搜索(DFS)的算法能够找到一个拓扑排序。

5:强连通分量 strong components

5.1:定义

(PPT 和书本内容一致,直接把书本的内容贴出来)

定义:

![image-20240309141811365]

性质:

![image-20240309141859632]
image-20240309141927944

5.2:无向图连通分量 vs. 有向图强连通分量

![L13-42DirectedGraphs_52]

5.3:强连通分量算法简史

![L13-42DirectedGraphs_55]

1960年代:核心运筹学问题。

  • 广泛研究,出现了一些实用算法。
  • 其复杂性尚未被充分理解。

1972年:线性时间的深度优先搜索(DFS)算法(由 Tarjan 提出)。

  • 经典算法,在算法领域占有重要地位。
  • 算法难度级别相当于“Algs4 进阶版”。
  • 显示了深度优先搜索在广泛应用场景下的重要性和实用性。

1980年代:简易两遍线性时间算法(Kosaraju-Sharir 算法)。

  • 教授课程时忘记带讲义,为了授课临时开发出该算法!
  • 后来发现该算法实际上在 1972 年的俄罗斯科学文献中已有记载。

1990年代:更多简易的线性时间算法出现。

  • Gabow 改进了原有的运筹学算法。
  • Cheriyan 和 Mehlhorn 设计了一种单遍线性时间算法,这是 LEDA 项目所需的关键技术。

Prof. Sedgewick 总结:

So this story indicates, even from fundamental problems in graph processing, there’s algorithms out there still waiting to be discovered. And this algorithm is a good example of that.

因此,这个故事表明,即使在图形处理的基本问题中,仍然存在有待发现的算法。而上述提到的算法恰好是一个很好的例子。

5.4:Kosaraju-Sharir 算法

5.4.1:直觉

![L13-42DirectedGraphs_56]

**反向图:**原图 G 中的强连通分量与反向图 GR 中的相同。

**核 DAG(Kernel DAG):**将每个强连通分量收缩成单个顶点。

思路:

  • 在核 DAG 中计算拓扑排序(采用逆后序)。
  • 以逆拓扑顺序考虑顶点,并运行深度优先搜索(DFS)。

书中的描述:

![image-20240309145233298]

5.4.2:demo 演示

![image-20240309145412029]

阶段1: 在 GR 图中计算逆后序排列。
阶段2: 在 G 图中运行深度优先搜索,按照 GR 图中的逆后序排列顺序访问未标记的顶点。

初始状态:

![image-20240309145554570]

5.4.2.1:第一阶段

在反向图 GR 图中计算逆后序排列。

反向图 GR

![image-20240309150628583]

逆后序排列演示在前面拓扑排序刚说完,就不一一截图了,大致搜索过程如下(* 粗体 代表搜索完成放入后序):

0 —— 6 —— 8

8 —— 6 —— 7

7 —— 6

6 —— 0 —— 2 —— 4 —— 11 —— 9 —— 12 —— 10

10 —— 12

12 —— 9

9 —— 11

11 —— 4 —— 5 —— 3

3 —— 5

5 —— 4

4 —— 2

2 —— 0

1

检查确保所有顶点都已经被标记。

第一阶段结束后的结果:

![L13-42DirectedGraphs_58]

5.4.2.2:第二阶段

在 G 图中运行深度优先搜索,按照 GR 图中的逆后序排列顺序访问未标记的顶点。

原始图 G:

![image-20240309151559229]

以第一阶段结果 1 0 2 4 5 3 11 9 12 10 6 7 8 进行搜索。

搜索顶点 1:

![image-20240309151936900]

没有其他顶点,强连通分量编号标记为 0,完成搜索。

![image-20240309152114934]

同理,搜索其他分量,大致过程如下:

0 —— 5 —— 4 —— 3 —— 2

![image-20240309152415181]

顶点 0、2、3、4、5 为强连通分量,标记为 1。

11 —— 12 —— 9 —— 10

![image-20240309152706706]

顶点 9、10、11、12 为强连通分量,标记为 2。

6 —— 8

![image-20240309153115846]

顶点 6、8 为强连通分量,标记为 3。

7

![image-20240309153139893]

顶点 7 为强连通分量,标记为 4。

完成所有的连通分量查询:

![L13-42DirectedGraphs_59]

5.4.3:过程小结

Kosaraju-Sharir 算法:计算强连通分量的简单(但神秘)算法。

![L13-42DirectedGraphs_60]

![L13-42DirectedGraphs_61]

5.4.4:证明

![image-20240309153852150]

证明具有一定的数学复杂性,但编码实现非常简单。

5.4.5:Java 实现

edu.princeton.cs.algs4.KosarajuSharirSCC

![image-20240309154159062]

![image-20240309154252073]

edu.princeton.cs.algs4.KosarajuSharirSCC#dfs

![image-20240309154328483]

edu.princeton.cs.algs4.KosarajuSharirSCC#stronglyConnected

![image-20240309154408874]

6:有向图处理小结

![L13-42DirectedGraphs_65]

(完)

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
本书带完整书签 第1章 基础 1 1.1 基础编程模型 4 1.1.1 Java程序的基本结构 4 1.1.2 原始数据类塑与表达式 6 1.1.3 语句 8 1.1.4 简便记法 9 1.1.5 数组 10 1.1.6 静态方法 12 1.1.7 API 16 1.1.8 字符串 20 1.1.9 输入输出 21 1.1.10 二分査找 28 1.1.11 展望 30 1.2 数据抽象 38 1.2.1 使用抽象数据类型 38 1.2.2 抽象数据类型举例 45 1.2.3 抽象教据类型的实现 52 1.2.4 更多抽象数据类型的实现 55 1.2.5 数据类型的设计 60 1.3 背包、队列和栈 74 1.3.1 API 74 1.3.2 集合类數据类型的实现 81 1.3.3 链表 89 1.3.4 综述 98 1.4 算法分析 108 1.4.1 科学方法 108 1.4.2 观察 108 1.4.3 数学模型 112 1.4.4 增长数量级的分类 117 1.4.5 设计更快的算法 118 1.4.6 倍率实验 121 1.4.7 注意事项 123 1.4.8 处理对于输入的依赖 124 1.4.9 内存 126 1.4.10 展望 129 1.5 案例研究:union-find算法 136 1.5.1 动态连通性 136 1.5.2 实现 140 1.5.3 展望 148 第2章 排序 152 2.1 初级排序算法 153 2.1.1 游戏规则 153 2.1.2 选择排序 155 2.1.3 插入排序 157 2.1.4 排序算法的可视化 159 2.1.5 比较两种排序算法 159 2.1.6 希尔排序 162 2.2 归并排序 170 2.2.1 原地归并的抽象方法 170 2.2.2 自顶向下的归并排序 171 2.2.3 自底向上的归并排序 175 2.2.4 排序算法的复杂度 177 2.3 快速排序 182 2.3.1 基本算法 182 2.3.2 性能特点 185 2.3.3 算法改进 187 2.4 优先队列 195 2.4.1 API 195 2.4.2 初级实现 197 2.4.3 堆的定义 198 2.4.4 堆的算法 199 2.4.5 堆排序 205 2.5 应用 214 2.5.1 将各种數据排序 214 2.5.2 我应该使用啷种排序算法 218 2.5.3 问题的归约 219 2.5.4 排序应用一览 221 第3章查找 227 3.1 符号表 228 3.1.1 API 228 3.1.2 有序符号表 230 3.1.3 用例举例 233 3.1.4 无序链表中的顺序查找 235 3.1.5 有序數组中的二分查找 238 3.1.6 对二分査找的分析 242 3.1.7 预览 244 3.2 二叉查找树 250 3.2.1 基本实现 250 3.2.2 分析 255 3.2.3 有序性相关的方法与删除操作 257 3.3 平衡査找树 269 3.3.1 2-3査找树 269 3.3.2 红黑二叉查找树 275 3.3.3 实现 280 3.3.4 删除操作 282 3.3.5 红黑树的性质 284 3.4 散列表 293 3.4.1 散列函数 293 3.4.2 基于拉链法的散列表 297 3.4.3 基于线性探测法的散列表 300 3.4.4 调整教组大小 304 3.4.5 内存使用 306 3.5 应用 312 3.5.1 我应该使用符号表的哪种实现 312 3.5.2 集合的API 313 3.5.3 字典类用例 315 3.5.4 索引类用例 318 3.5.5 稀疏向量 322 第4章 图 329 4.1 无向图 331 4.1.1 术语表 331 4.1.2 表示无向图的数据类型 333 4.1.3 深度优先搜索 338 4.1.4 寻找路径 342 4.1.5 广度优先搜索 344 4.1.6 连通分量 349 4.1.7 符号图 352 4.1.8 总结 358 4.2 有向图 364 4.2.1 术语 364 4.2.2 有向图的数据类型 365 4.2.3 有向图中的可达性 367 4.2.4 环和有向无环图 369 4.2.5 有向图中的强连通性 378 4.2.6 总结 385 4.3 最小生成树 390 4.3.1 原理- 391 4.3.2 加权无向图的数据类型 393 4.3.3 最小生成树的API和测试用例 396 4.3.4 Prim算法 398 4.3.5 Prim算法的即时实现 401 4.3.6 Kruskal算法 404 4.3.7 展望 407 4.4 最短路径 412 4.4.1 最短路径的性质 413 4.4.2 加权有向图的数据结构 414 4.4.3 最短路径算法的理论基础 420 4.4.4 Dijkstra算法 421 4.4.5 无环加权有向图中的最短路径算法 425 4.4.6 一般加权有向图中的最短路径问题 433 4.4.7 展望 445 第5章 字符串 451 5.1 字符串排序 455 5.1.1 键索引计数法 455 5.1.2 低位优先的字符串排序 458 5.1.3 高位优先的字符串排序 461 5.1.4 三向字符串快速排序 467 5.1.5 字符串排序算法的选择 470 5.2 单词查找树 474 5.2.1 单词查找树 475 5.2.2 单词查找树的性质 483 5.2.3 三向单词查找树 485 5.2.4 三向单词查找树的性质 487 5.2.5 应该使用字符串符号表的哪种实现 489 5.3 子字符串查找 493 5.3.1 历史简介 493 5.3.2 暴力子字符串査找算法 494 5.3.3 Knuth-Morris-Pratt子字符串查找算法 496 5.3.4 Boyer-Moore字符串查找算法 502 5.3.5 Rabin-Karp指纹字符串查找算法 505 5.3.6 总结 509 5.4 正则表达式 514 5.4.1 使用正则表达式描述模式 514 5.4.2 缩略写法 516 5.4.3 正则表达式的实际应用 517 5.4.4 非确定有限状态自动机 518 5.4.5 模拟NFA的运行 520 5.4.6 构造与正则表达式对应的NFA 522 5.5 数据压缩 529 5.5.1 游戏规则 529 5.5.2 读写二进制数据 530 5.5.3 局限 533 5.5.4 热身运动:基因组 534 5.5.5 游程编码 537 5.5.6 霍夫曼压缩 540 第6章背景 558 索引 611
个人觉得是我见过的最简单易懂的算法入门书籍。 以前搜刮过几本算法竞赛书,但是难度终归太大【好吧,其实是自己太懒了】。 略翻过教材,大多数水校的教材,大家懂的。好一点的也是那本国内的经典,不是说它写的不好,只是没有这一本好。 本书Java实现,配有大量的图解,没有一句难懂的话,而且全都是模块化实现。 讲的都是实用算法,没有那些高大上听着名字就让人感到很害怕的东西,个人觉得比CLRS实用性要强,更加适合入门的学习。 大一,推荐这本书入门 【有C语言基础即可,自己去搜索下如何用Java写出Hello World就没有问题】 大二,推荐这本书从头到尾好好读一遍,做下上千道的课后习题 【后面的有点小难度,但是难度不大值得一做,听起来很多的样子,用心去做,相信很快就可以做完的】。 大三,推荐这本书,重新温习已知算法,为找工作,考研做准备。 【可以试着自己在纸上全部实现一遍】 大四,依旧推荐这本书,没事重温经典,当手册来查也不错。 Sedgwick 红黑树的发现者,Donald E.Knuth 的得意门生,对各种算法都有比较深入的研究,他的书,我想不会太差。 也许对于数据结构的学习涉及的内容比较少,没有动态规划,图论也只是讲了很基础的东西,字符串中KMP弄的过于复杂(对比于acm)。但是瑕不掩瑜,对于绝大部分内容真的讲的超级清楚,完美的图解,就像单步调试一样,也许是一本不需要智商就能看懂的算法书(习题应该略有难度,还没有做,打算上Princeton的公开课时同步跟进)。至少这是一本让我这个算法渣渣看了爱不释手,怦然心动的书。 完美学习资源: 官方主页:http://algs4.cs.princeton.edu/home/ Coursera公开课:https://www.coursera.org/course/algs4partI (听说已经开课两期了,最近即将开课的时间是2014/09/05号那期,希望有兴趣的同学一起来学习)。 MOOC平台(笔记、讨论等): http://mooc.guokr.com/course/404/Algorithms--Part-I/ http://mooc.guokr.com/course/403/Algorithms--Part-II/ 不得不吐槽,他的lecture比他的书好,他本人讲的课更是一绝。 互补课程: 斯福坦的Algorithms: Design and Analysis, http://mooc.guokr.com/course/157/Algorithms--Design-and-Analysis--Part-1/ 快毕业了才接触到豆瓣和MOOC,看到很多经典的书籍都是推荐大学一二年级的学生看,每每想到自己却连书皮都没有摸过,就深感惭愧。 我们都老的太快,却聪明得太迟。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MichelleChung

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值