一 概述
深度优先搜索(Depth-First-Search,DFS)类似于树的先序遍历。它遵循的搜索策略是尽可能"深"地搜索一个图。
二 深度优先搜索的基本思想
- 首先访问图中某一起始顶点v;
- 接着由顶点v出发,访问与v邻接且未被访问的任意顶点Wi;
- 然后访问与Wi邻接且未被访问的任一顶点Yi;
- 若Wi没有邻接且未被访问的顶时,退回到它的上一层顶点v;
- 继续重复上述过程。当不能再继续向下访问时,依次退回到最近被访问的顶点,若它还有邻接顶点未被访问过,则从该点开始继续上述搜索过程,直至图中所有顶点均被访问过为止。
上述过程中涉及到了递归,即DFS算法是一个递归算法,需要借助一个递归工作栈,故其空间复杂度为O(|V|)。
三 深度优先搜索算法的实际案例步骤分析
如图所示,深度优先访问的步骤为:
先访问起始顶点1。
接着从顶点1出发,访问与顶点1邻接且未被访问的任意顶点2或3,此处先访问顶点2。
然后访问与顶点2相邻且未被访问的顶点4或5,此处访问顶点4。
访问顶点4之后,对其邻接顶点5进行访问。
当访问与顶点5相邻且未被访问的顶点时,发现顶点不存在,所以依次退回最近访问的顶点,当退至顶点4,返现未被访问的顶点7,并进行访问。
访问与顶点4相邻且未被访问的另一个顶点7。
当访问与顶点7相邻且未被访问的顶点时,发现顶点不存在,所以依次退回最近访问的顶点,当退至顶点1时,返现未被访问的顶点3,并进行访问。
最后访问顶点与顶点3相邻且未被访问的顶点6,这样就完成了该图的深度优先算法的操作。
四 深度优先搜索算法的实现
根据上述实例分析可知,图的深度优先搜索算法的实现类似于树的先序优先遍历算法。由此,图的深度优先搜索算法可以利用递归方法来实现,所以我们需要利用栈来完成递归操作的实现。同时利用一个辅助标记数组对已经访问的顶点进行标记。
bool visited[MAX_TREE_SIZE]
//对非连通图进行各个顶点访问
void DFSTraverse(Graph G){
//初始化visited数组
for(int i = 0; i < G.vexnum; ++i){
visited[i] = FALSE;
}
//实现非连通图的广度优先搜索的一个循环
for(int i = 0; i < G.vexnum; ++i){
if(!visited[i]){
DFS(G,i);
}
}
}
//G表示连通图,v表示搜索顶点的编号
void DFS(Graph G,int v){
//访问顶点
visit(v);
//将改顶点在辅助标记数组中的值设置为TRUE
visited[v] = TRUE;
//找到所有符合要求的邻接顶点
//FirstNeighbor求某个顶点的第一个邻接顶点,当此时的w为-1表示无邻接顶点,当w>0成立时,该w为邻接顶点的编号
//NextNeighbor求某个顶点的其他邻接顶点。
for(w = FirstNeighbor(G,v); w >= 0; w = NextNeighbor(G,v,w))
if(!visited[w]){
DFS(G,w);
}//if
}
对于通过邻接矩阵构造的图中DFS(BFS)序列均是唯一的,而通过邻接表构造的图DFS(BFS)序列是不唯一。
五 DFS算法的性能分析
DFS算法的空间复杂度:因为最坏情况下,利用的空间栈大小为结点的数量,故该算法的空间复杂度为O(|V|)。
DFS算法的时间复杂度:
通过邻接矩阵法的图:
DFS的时间复杂度为O(|V|*|V|)。
原因:我们在DFS中访问某个顶点时,我们需要一个循环来寻找某个顶点的所有邻接顶点,在邻接矩阵法中找寻某顶点的所有邻接顶点即为找到该顶点在矩阵中对应的行。当矩阵中某值为1的时候,该值对应的顶点即为要寻找的邻接顶点。当在邻接矩阵法的图中通过DFS算法找到所有顶点的邻接顶点的时间复杂度为O(|V| * |V|)。
通过邻接表法的图:
DFS的时间复杂度为O(|V| + |E|)。
原因:我们在访问所有顶点的邻接顶点时,需要通过访问其边进行访问。所以当在邻接表法的图中通过DFS算法找到所有顶点的邻接顶点的时间复杂度为O(|V| + |E|)。