题意概述
- n个结点,m条边,边所连接的两个结点之间可相互到达(无向边),且一条边只可走一次
- 若有从1号结点开始的回路则返回true,否则返回false
相关知识
- 图的深度优先搜索模板(邻接表实现)
const int MAXV=1000; const int inf=0x3fffffff; int n; vector<vector<int> >e;//或者vector<int>e[MAXV]; bool vis[MAXV]; void DFS(int u,int depth){//u为当前访问顶点标号,depth为深度 vis[u]=true;//置u已被访问 for(int i=0;i<e[u].size();i++){//枚举u所能到达的每个分支顶点 int v=e[u][i]; if(vis[v]==false)DFS(v,depth+1);//如果v未访问,且u可达v,访问v深度+1 } } void DFSTrave(){//遍历图 for(int u=0;u<n;u++){//对每个顶点u if(vis[u]==false)DFS(u,1);//如果u未访问,访问它所在的联通块,1表示初始为第一层 } }
- 图的广度优先搜索模板(邻接表实现)
const int MAXV=1000; const int inf=0x3fffffff; int n; vector<int>e[MAXV]; bool vis[MAXV]; void BFS(int u){ queue<int>q;//定义队列q,并让起点入队 q.push(u); vis[u]=true; while(!q.empty()){//在队列非空时,不断取队首元素访问 int u=q.front(); q.pop(); for(int i=0;i<e[u].size();i++){//让队首元素能到达结点入队 int v=e[u][i]; if(vis[v]==false){ q.push(v); vis[v]=true; } } } } void BFSTrave(){//遍历图 for(int u=0;u<n;u++){//对每个顶点u if(vis[u]==false)BFS(u);//如果u未访问,访问它所在的联通块 } }
方法一:DFS
思路与具体做法
- 用邻接表存储图,然后从1号结点开始深度优先搜索遍历连通图,并对访问过的点用vis数组进行标记,直到遍历完所有点;
- 递归起点1号结点设深度为1,若某点的后继结点为1且当前递归深度不为2,即为存在从1号结点开始的回路。
class Solution { public: int ans=0;//是否有回路 bool DFS(int u,int depth,vector<bool>&vis,vector<vector<int> >&e){ vis[u]=true;//标记当前点u已访问 for(int i=0;i<e[u].size();i++){//遍历u所能到达的所有点 int v=e[u][i];//u的后继点 if(v==1&&depth!=1)return ++ans;//最终能回到点1 if(vis[v]==false)DFS(v,depth+1,vis,e);//u的后继点未访问,则访问它 } return ans; } string solve(vector<int>& param, vector<Point>& edge) { int n=param[0],m=param[1];//n个结点,m条边 vector<bool>vis(n+1,false);//判断各点有没有访问过 vector<vector<int> >e(n+1);//邻接表存图 for(int i=0;i<m;i++){//读入边 int x=edge[i].x,y=edge[i].y; e[x].push_back(y); e[y].push_back(x); } if(DFS(1,0,vis,e))return "Yes"; else return "No"; } };
时间复杂度和空间复杂度分析
时间复杂度:,m为边数,n为结点数,初始建图遍历长度为m的边集,递归中搜索了n个结点
空间复杂度:,n为结点数,最坏情况任意两点间有边,此时图最大
方法二:BFS
思路与具体做法
- 广度优先搜索遍历连通图,对搜索起点的所有孩子结点标记上不同“路标”,搜索到其他结点时均继承父结点的“路标”,同时对访问过的点进行标记;这里可用vis数组同时进行是否访问过标记与加入路标,具体做法为对访问过点,加入对应路标;
- 如果存在一个访问过的结点,有两个冲突的“路标”,即该点属于两条从根结点延伸出来的路径,即可证,有环路;
class Solution { public: bool BFS(int u,vector<int>&vis,vector<vector<int> >&e){ queue<int>q;//定义队列q,并让起点入队 q.push(u); vis[u]=true; int id=1; while(!q.empty()){//在队列非空时,不断取队首元素访问 int u=q.front(); q.pop(); for(int i=0;i<e[u].size();i++){//让队首元素能到达结点入队 int v=e[u][i];//u的后继点 if(vis[v]==false){//后继点未访问则访问它 q.push(v); if(u==1)vis[v]=id++;//从根结点开始进行分叉标记 else vis[v]=vis[u];//除根结点的子结点外的其他子结点继承父结点的路标 }else if(vis[v]!=vis[u]){//访问过,且该点属于两条从根结点延伸出来的路径,即可证,有环路 return true; } } } return false; } string solve(vector<int>& param, vector<Point>& edge) { int n=param[0],m=param[1];//n个结点,m条边 vector<int>vis(n+1,0);//判断各点有没有访问过 vector<vector<int> >e(n+1);//邻接表存图 for(int i=0;i<m;i++){//读入边 int x=edge[i].x,y=edge[i].y; e[x].push_back(y); e[y].push_back(x); } if(BFS(1,vis,e))return "Yes"; else return "No"; } };
时间复杂度和空间复杂度分析
时间复杂度:,m为边数,n为结点数,初始建图遍历长度为m的边集,BFS中搜索了n个结点
空间复杂度:,n为结点数,最坏情况任意两点间有边,此时图最大