"《算法导论》之‘图’":深度优先搜索、宽度优先搜索及连通分量

  本文兼参考自《算法导论》及《算法》。

  以前一直不能够理解深度优先搜索和广度优先搜索,总是很怕去碰它们,但经过阅读上边提到的两本书,豁然开朗,马上就能理解得更进一步。

  1. 深度优先搜索

   1.1 迷宫搜索

  在《算法》这本书中,作者写了很好的一个故事。这个故事让我马上理解了深度优先搜索的思想。

  如下图1-1所示,如何在这个迷宫中找到出路呢?方法见图1-2.

  

  图1-1 等价的迷宫模型

  探索迷宫而不迷路的一种古老办法(至少可以追溯到忒修斯和米诺陶的传说)叫做Tremaux搜索,如下图所示。要探索迷宫中的所有通道,我们需要:

  1)选择一条没有标记过的通道,在你走过的路上铺一条绳子;

  2)标记所有你第一次路过的路口和通道;

  3)当来到一个标记过的路口时,用绳子回退到上个路口;

  4)当回退到的路口已没有可走的通道时继续回退。

  绳子可以保证你总能找到一条出路,标记则能保证你不会两次经过同一条通道或同一个路口。

  

  图1-2 Tremaux探索

   1.2 深度优先搜索

  《算法》作者给出的Java代码如下:

 1 public class DepthFirstSearch
 2 {
 3     private boolean[] marked;
 4     private int count;
 5
 6     public DepthFirstSearch(Graph G, int s)
 7     {
 8         marked = new boolean[G.V()];
 9         dfs(G, s);
10     }
11
12     private void dfs(Graph G, int v)
13     {
14         marked[v] = true;
15         count++;
16         for(int w : G.adj(v))
17         {
18             if(!marked[w])
19             {
20                 dfs(G, w);
21             }
22         }
23     }
24
25     public boolean marked(int w)
26     {
27         return marked[w];
28     }
29
30     public int count()
31     {
32         return count;
33     }
34 }

DFS.java

  具体例子如下图1-3:

  

  图1-3 使用深度优先探索的轨迹,寻找所有和顶点0连通的顶点

   1.3 寻找路径

  《算法》作者给出的Java代码如下:

 1 public class DepthFirstPaths
 2 {
 3     private boolean[] marked; // Has dfs() been called for this vertex?
 4     private int[] edgeTo; // last vertex on known path to this vertex
 5     private final int s; // source
 6     public DepthFirstPaths(Graph G, int s)
 7     {
 8         marked = new boolean[G.V()];
 9         edgeTo = new int[G.V()];
10         this.s = s;
11         dfs(G, s);
12     }
13
14     private void dfs(Graph G, int v)
15     {
16         marked[v] = true;
17         for (int w : G.adj(v))
18             if (!marked[w])
19             {
20                 edgeTo[w] = v;
21                 dfs(G, w);
22             }
23     }
24
25     public boolean hasPathTo(int v)
26     {
27         return marked[v];
28     }
29
30     public Iterable<Integer> pathTo(int v)
31     {
32         if (!hasPathTo(v)) return null;
33         Stack<Integer> path = new Stack<Integer>();
34         for (int x = v; x != s; x = edgeTo[x])
35             path.push(x);
36         path.push(s);
37         return path;
38     }
39 }

DFS.java

  

  图1-4 pathTo(5)的计算轨迹

  

  图1-5 使用深度优先搜索的轨迹,寻找所有起点为0的路径

  2. 广度优先搜索

   2.1 迷宫搜索

   深度优先搜索就好像是一个人在走迷宫,广度优先搜索则好像是一组人在一起朝各个方向走这座迷宫,每个人都有自己的绳子。当出现新的叉路时,可以假设一个探索者可以分裂为更多的人来搜索它们。当两个探索者相遇时,会合二为一(并继续使用先到达者的绳子)。如下图2-1:

  

  图2-1 广度优先的迷宫搜索

   2.2 广度优先搜索

  《算法》作者给出的使用广度优先搜索查找图中的路径的Java代码如下:

 1 public class BreadthFirstPaths
 2 {
 3     private boolean[] marked;     // Is a shortest path to this vertex known?
 4     private int[] edgeTo;         // last vertex on known path to this vertex
 5     private final int s;         // source
 6
 7     public BreadthFirstPaths(Graph G, int s)
 8     {
 9         marked = new boolean[G.V()];
10         edgeTo = new int[G.V()];
11         this.s = s;
12         bfs(G, s);
13     }
14
15     private void bfs(Graph G, int s)
16     {
17         Queue<Integer> queue = new Queue<Integer>();
18         marked[s] = true;     // Mark the source
19         queue.enqueue(s);     // and put it on the queue.
20         while (!q.isEmpty())
21         {
22             int v = queue.dequeue();     // Remove next vertex from the queue.
23             for (int w : G.adj(v))
24             if (!marked[w])             // For every unmarked adjacent vertex,
25             {
26                 edgeTo[w] = v;             // save last edge on a shortest path,
27                 marked[w] = true;         // mark it because path is known,
28                 queue.enqueue(w);         // and add it to the queue.
29             }
30         }
31     }
32
33     public boolean hasPathTo(int v)
34     {
35         return marked[v];
36     }
37
38     public Iterable<Integer> pathTo(int v)
39     // Same as for DFS.
40 }

BFS.java

  一个例子如下:

  

  图2-2 使用广度优先搜索寻找所有起点为0的路径的结果

  

  图2-3 使用广度优先搜索的轨迹,寻找所有起点为0的路径

  3. 连通分量

  具体代码已经托管到Github.

时间: 2024-10-10 16:11:50

"《算法导论》之‘图’":深度优先搜索、宽度优先搜索及连通分量的相关文章

挑战程序2.1.5 穷竭搜索&gt;&gt;宽度优先搜索(练POJ3669)

先对比一下DFS和BFS         深度优先搜索DFS                                   宽度优先搜索BFS 明显可以看出搜索顺序不同. DFS是搜索单条路径到底部,再回溯. BFS是搜索近的状态,直到底部,一般在求解最短路径或者最短步数上应用. BFS要用到队列呢.. 队列的用法看一看http://blog.csdn.net/cindywry/article/details/51919282 练习题系列--------------------- 题目:p

基础算法 --- BFS(广度优先搜索/宽度优先搜索)

个人理解 BFS是一种最简便的图搜索算法,通过遍历整张图直到找到目标节点: 从算法的角度看,所有因为展开节点而得到的字节点会被存储到一个FIFO的数据结构中,被遍历过的节点存储在一个容器中(一般是一个set),防止重复搜索 图解 如图所示,我们想要获取从S到E节点的最短路径,运用BFS,如何处理?? 主要思想是:从节点S开始将其所有临近子节点存放到一个队列中,然后标记这些节点到最初顶点S的距离为1: 然后根据队列的先进先出特定,出队列,判断是否为目标节点:如果不是,将出队节点的临近子节点入队,直

算法7-4:宽度优先搜索

宽度优先搜索的基本思想就是先将源点添加到一个队列中, 每次从队列中取出一个顶点,将该顶点的邻居顶点中没有拜访过的顶点都添加到队列中,最后直到队列消耗完毕为止. 应用 宽度优先搜索可以解决最短路径问题.而最短路径算法在互联网中应用非常广泛,尤其是路由这块.因为路由追求的是高效,所以每个路由路径都是通过最短路径计算出来的.如果没有最短路径算法,我们可能就享受不到这么快的网速了. 另外,宽度优先搜索在快递行业也会用到,用于计算最短路径. 代码 import java.util.Stack; /** *

广度/宽度优先搜索(BFS)详解

广度/宽度优先搜索(BFS) [算法入门] 1.前言 广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历策略.因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域,故得名. 一般可以用它做什么呢?一个 广度/宽度优先搜索(BFS) 算法导论里边会给出不少严格的证明,我想尽量写得通俗一点,因此采用一些直观的讲法来伪装成证明,关键的point能够帮你get到就好. 2.图的概念 刚刚说的广度优先搜索是连通图的一种遍历策略,那就有必要将图先简单解释一下.

宽度优先搜索

Breadth First Search 宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型.Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想.其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果.换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止. 与树的层序遍历一样 遍历结果为 1 2 3 4 5 6 .....21 22 操作: 根节点入队: wh

层层递进——宽度优先搜索(BFS)

问题引入 我们接着上次“解救小哈”的问题继续探索,不过这次是用宽度优先搜索(BFS). 注:问题来源可以点击这里 http://www.cnblogs.com/OctoptusLian/p/7429645.html 最开始小哼在入口(1,1)处,一步之内可以到达的点有(1,2)和(2,1). 但是小哈并不在这两个点上,那小哼只能通过(1,2)和(2,1)这两点继续往下走. 比如现在小哼走到了(1,2)这个点,之后他又能够到达哪些新的点呢?有(2,2).再看看通过(2,1)又可以到达哪些点呢?可以

(总结)宽度优先搜索(Breadth First Search)

ACM入门最经典的开局一般都是宽搜. 宽度优先搜索(以下均简称bfs)一般用于树和图的搜索,在ACM中属于比较基础的技巧,因此需要非常熟练的掌握. 那么从最基础的bfs开始讲起.在一个迷宫中,有一个起点和一个终点(出口),和一些障碍物(无法通过). 比如下图

宽度优先搜索(BFS)

宽度优先搜索,又称为广度优先搜索,简称BFS 搜索过程:从初始结点开始,逐层向下扩展,即第n层搜索未完成,不得进入下一层搜索 一.初始结点入队,进入循环 二.取出队列的第一个元素 三.判断该结点是不是目标结点,如果是目标结点,则问题解决,跳出循环 四.如果该结点不是目标结点,判断其是否能够扩展,若不能,跳到步骤二 五.如果该结点能扩展,将其子结点放入队列的尾部 六.跳到步骤二 用一个经典的例子(走迷宫)来感受下 给定一个二维数组 int a[10][10] = {0 , 1 , 0 , 0 ,

算法导论22.3深度优先搜索 练习总结 (转载)

22.3-1 画一个 3*3 的网格,行和列的抬头分别标记为白色.灰色和黑色,对于每个表单元 (i, j),请指出对有向图进行深度优先搜索的过程中,是否可能存在一条边,链接一个颜色为 i 的结点和一个颜色为 j 的结点.对于每种可能的边,指明该种边的类型.另外,请针对无向图的深度优先搜索再制作一张这样的网格. ANSWER:   22.3-2 给出深度优先搜索算法在图 22-6 上的运行过程.假定深度优先搜索算法的第 5~7 行的 for 循环是以字母表顺序依次处理每个结点,假定每条邻接链表皆以