上一节课我们介绍了有关图的基本操作,那么除了上节课我们介绍的那些相关基本操作之外,还有一种非常重要的操作就是有关图的遍历。那么图的遍历分为两种,有广度优先搜索,也就是广度优先遍历。还有深度优先搜索。本节课我们就来学习图的广度优先搜索。
那么首先我们先来了解介绍一下,图的遍历是什么呢?什么是图的遍历。其实它图的遍历与树的遍历以及线性表的遍历是类似的。它是从图中某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次,并且只访问一次。这就是图的遍历。那么提到图的遍历,提到广度优先搜索,其实它与我们之前学习过树的层次遍历的顺序是类似的。
那么首先我们先来复习一下,树的层次遍历。那么这样编号的顺序是不是就是树的层次遍历的顺序啊。
我们按照编号的顺序依次地访问树中的每一个结点,并且每个结点只访问1次。这就是树的层次遍历的过程。那么其实,如果我们把该树看作成一个图的话,树的层次遍历的过程就是图的广度优先搜索的这样一个顺序。
那么接下来我们就来介绍一下图的广度优先搜索。首先我们要访问起始的一个顶点,顶点V。接着我们由该顶点出发,依次访问V的各个未被访问过的的邻接顶点w1,w2,…wi。w1-wi都是V的各个未被访问过的邻接顶点。好,接着应该如何访问呢?我们要访问它们的所有未被访问过的邻接顶点。那么接着我们只要重复该过程,从这些访问过的顶点出发,再访问它们未被访问过的一些顶点,然后依此类推就可以访问该图中的所有顶点了。这就是广度优先搜索的过程。我们发现在广度优先搜索当中,其实我们是优先先访问靠近起始顶点的那些顶点的,这一点与树的层次遍历是一致的。那么广度优先搜索和树的层次遍历是完全一样的吗?我们可以直接把树的层次遍历算法直接搬到广度优先搜索上吗?答案是不是的。
我们发现在广度优先搜索的描述过程当中,我们强调了一定是未被访问过的各个邻接顶点。好,为什么要强调的是未被访问的邻接顶点呢?我们来看这样一个例子。这是我们刚刚举的那一个图的例子。我们对它进行广度优先搜索。这与它对应树的层次遍历的顺序是一致的。
那么此时我们在顶点4、
本节课我们来学习深度优先搜索。
首先我们先来复习一下上节课所学习的广度优先搜索。广度优先搜索类似于树的层次遍历,假设我们现在有一个初始顶点1,那么我们从该顶点出发,依次地访问该图当中的每一个顶点。那么我们知道广度优先搜索是优先访问离1较近的那一些顶点的,这就是广度优先搜索。
好,广度优先搜索与树的层次遍历比较类似。那深度优先搜索呢?其实深度优先搜索与树的先序遍历访问比较相似。我们先来复习一下树的先序遍历。
首先这是一棵树,然后我们从根结点1出发,来访问根结点。然后访问根结点的一棵子树的根结点,也就是顶点2。然后依旧访问顶点2的一棵子树的根结点,也就是顶点4。然后按照这样的规则访问顶点7。
那么顶点4的所有的结点已经访问完毕了,所以我们要访问顶点2的另一棵子树的根结点也就是顶点5。
然后按照这样的规则我们访问了该树当中的所有顶点。
依旧,如果把树看成一个图的的话,那么这样的顺序其实就是这样一个图它的深度优先搜索的顺序。我们在这样一个类似树的图的例子当中发现,是不是广度优先搜索和深度优先搜索与它们的名字非常符合啊。广度优先搜索是按照图的宽度的范围这样进行扩展顶点的,
深度优先搜索则是按照这样一条路径的深度的走向去访问顶点的。
好,接下来我们就来学习一下,深度优先搜索在书中是怎样描述的。那么这里深度优先搜索它的缩写为DFS,D代表的是深度的意思。首先第一步我们要访问起始的这个顶点V,然后呢然后我们要从V出发,访问V的任意一个邻接且未被访问过的邻接顶点wi。大家要注意一下,这里是任意,任意一个符合条件的邻接顶点都可以,没有次序的要求。好,接着我们要从wi出发,再访问满足条件的邻接顶点。这里也是任意的一个邻接且未被访问的邻接顶点yi。那么如果wi没有符合这样的条件的邻接顶点呢,我们要退回到它的上一层顶点,也就是V。我们是从V到wi的。然后我们只要重复这样的过程,直到所有顶点被访问为止。这样我们就实现了深度优先搜索DFS。
那么大家要注意一下,这里与BFS广度优先搜索一样,我们也强调了一定是未被访问过的这些邻接顶点。那么也是同样的要求,我们要为了避免某一个顶点访问了多次,这样就不符合图的遍历的定义了。我们一起来看一下这个图的例子。
我们从顶点1出发,首先呢,我们要访问起始顶点V,也就是访问这个起始顶点顶点1。然后我们要访问V的某一个邻接且未被访问过的邻接顶点,这里有顶点2和顶点3,我们访问哪一个都可以。那么我们访问顶点2。然后我们从顶点2出发,来访问顶点2的未被访问过的任意的一个邻接顶点,这里我们选取了顶点4。那么接着我们按照相同的步骤访问了顶点5。大家可能会有疑问,这里为什么我们又访问了顶点5呢?顶点5明明是顶点2的邻接顶点啊。我们发现,其实顶点5是不是也是顶点4的邻接顶点啊,这里我们是通过顶点4访问到顶点5的。接着我们发现顶点5是不是没有符合条件的邻接顶点了,所以我们返回到了它的上一层顶点也就是顶点4。然后我们依旧从顶点4开始进行访问,访问顶点4的一个任意一个邻接的且未被访问过的邻接顶点。这里我们只剩下了顶点7,所以我们要访问顶点7。然后顶点7依旧没有符合条件的顶点,我们退回到顶点4,顶点4也没有符合条件的顶点了,我们又退回到顶点2,依旧退回到顶点1。
那么顶点1有一个符合条件的顶点就是顶点3。这里顶点3没有被访问,且与1邻接。然后顶点3我们依旧要访问它的一个符合条件的邻接顶点,就是顶点6。好,这样我们就访问过了所有的顶点,实现了深度优先搜索。好,这就是DFS,深度优先搜索的一个过程。那么接下来我们就来讨论一下,如何实现它呢?上一节课实现广度优先搜索时,我们利用了一个队列还有一个标记数组来实现了广度优先搜索。因为队列可以实现树的层次遍历。
那么这里呢?这里的深度优先搜索,它与树的先序遍历比较像,那么大家联想一下,其实深度优先搜索它是不是可以用递归的形式来实现啊。那么这里,因为递归也转化为栈的形式,我们可以将先序遍历转化为栈的形式,所以深度优先搜索一定也可以利用栈来实现。
那么除了递归或者是栈,我们还缺了一个辅助的标记数组,这里与广度优先搜索是一样的。那么我们用这两种数据结构,就可以实现DFS的一个算法。
接下来我们来学习一下深度优先搜索是如何用代码来实现它的。那么这就是一个深度优先搜索DFS的代码。这里我们采用的是递归的形式。我们来看一下这个代码。首先