图的遍历就是不重不漏地访问每一个结点,具体方法有深度优先和广度优先
1 深度优先
习惯于先访问下标小的结点,虽然都是等价的
每个深度优先都对应一棵深度优先生成树,且不唯一
一路向下走,走到没有下一个时进行回溯
对于有向图,对于一个起点,可能无法回溯到所有结点,形成深度优先生成森林
//图的遍历--把每个结点访问一次
//深度优先搜索
//公有dfs
template<class TypeOfVer,class TypeOfEdge>
void adjListGraph<TypeOfVer,TypeOfEdge>::dfs()const{
bool*visited=new bool[Vers];//用visited记录已经访问过的结点
for(int i=0;i<Vers;i++) visited[i]=false;
cout<<"当前深度优先遍历序列为:"<<endl;
for(int i=0;i<Vers;++i){//可能一次并未访问所有点,跳出后继续以没有被访问的最小点为起始点进行深度优先搜索
if(visited[i]==true)continue;//找到第一个没有被访问过的结点
dfs(i,visited);//从未被访问的结点i出发dfs,调用私有dfs
cout<<endl;//每个连通分量一行
}
}
template<class TypeOfVer,class TypeOfEdge>
adjListGraph<TypeOfVer,TypeOfEdge>::dfs(int start,bool visited[])const
{
edgeNode*p=verList[start].head;
cout<<verList[start].ver<<'\t';
visted[start]=true;
while(p!=NULL)
{
if(visted[p->end]==false)
dfs(p->end,visited);//递归dfs
p=p->next;
}
}
此处难点是递归的应用
时间性能分析
若用邻接表,深度优先搜索会访问所有结点和边,即O(|V|+|E|)
若用邻接矩阵,深度优先搜索时间复杂度O(|V|**)
2 广度优先搜索
类似树的层次遍历(队列)
广度优先也有森林
template<class TypeOfVer,class TypeOfEdge>
adjListGraph<TypeOfVer,TypeOfEdge>::bfs(int start,bool visited[])const
{
bool *visited=new bool [Vers];//存储结点下标队列
int currentNode;
linkQueue<int>q;//
edgeNode*p;
for(int i=0;i<Vers;i++) visited[i]=false;
for(int i=0;i<Vers;i++)//造森林的语句
{
if(visited[i]==true) continue;
q.enQueue(i);
while(!q.isEmpty())
{
currentNode=i;
if(visited[currentNode]==true) continue;
cout<<verList[currentNode]<<'\t';
visited[cuurenNode]=true;
//邻居进队
p=verList[i].head;
while(p!=NULL)
{
if(visited[p->end]==false) q.enQueue(p->end);//没被访问过的入队
p=p->next;
}
}
cout<<endl;//每个广度优先树换行
}
}
时间复杂度分析
如果用邻接表存储,O(V+E)
用邻接矩阵O(V**2)
和深度优先搜索一致
4总结
深度优先–递归实现(私有函数中回溯,公有函数中建森林),相当于树的前序遍历
广度优先搜索–树的层次遍历,只有一个函数,两层循环包含建森林和用队列进行参差遍历
5 与无序表连通性关系
广度优先和深度优先都可以测试无序表的连通性,若连通,则只有一个生成树(只有一个连通分量)若不连通,则可得生成树森林