二、遍历图
1.bfs
(1)邻接矩阵的情况
有几个关键点:
①使用队列保证了层数浅的节点永远在层数深的节点之前出队,这样就不会出现一个浅层节点的相邻边还未遍历就去遍历一个深层节点的相邻边
②visited数组是记录已经入队的节点的,避免同一层的节点多次访问同一个下一层节点
int visited[num+10]; int Graph[num][num]={ {1,0,1}, {0,1,0}, {1,0,1} }; queue<int> q; void bfs(int start){ q.push(start); visited[start]=1; while(!q.empty()){ int vertax=q.front(); q.pop(); /* 判断队头的属性,对于搜索的问题,满足条件时退循环 */ for(int i=1;i<=num;i++){ if(i!=vertax&&Graph[vertax-1][i-1]!=0&&visited[i]==0){ q.push(i); visited[i]=1; } } } } int main(){ memset(visited,0,sizeof(visited)); for(int i=1;i<=N;i++){//此循环可以用来计算有几个图 if(visited[i]==1)continue; bfs(i); } return 0; }
(2)邻接表的情况
也有带权值与不带权值的区别,但是如果是把节点写为结构体的话,似乎很复杂,这里先放一放
#include<queue> vector<int> G[num+10]; int visited[num+10]; queue<int> q; void bfs(int start){ q.push(start); visited[start]=1; while(!q.empty()){ int vertax=q.front(); q.pop(); /* 判断条件,满足则退循环 */ for(int i=0;i<G[vertax].size();i++){ int point=G[vertax][i]; if(visited[point]==0){ visited[point]=1; q.push(point); } } } } int main(){ memset(visited,0,sizeof(visited)); for(int i=1;i<=num;i++){ if(visited[i]!=0)continue; bfs(i); } return 0; }
2.dfs
(1)递归写法:
这里只写一下邻接表的情况,其实邻接表与邻接矩阵的最大不同在于邻接矩阵需要先判断两个点之间是否有边,而邻接表直接判断访问过没有就可以了
vector<int> G[num+10]; int visited[num+10]; void dfs(int start){ visited[start]=1; for(int i=0;i<G[start].size();i++){ int vertax=G[start][i]; if(visited[vertax]==0){ dfs(vertax); } } } int main(){ memset(visited,0,sizeof(visited)); for(int i=1;i<=num;i++){ if(visited[i]!=0)continue; dfs(i); } return 0; }
(2)不递归的写法:
利用栈stack,关键点:
①is_push用来判断是否已经无路可走,当前点凡不能扩展则应出栈
②使用stack,原因是要持续往前走,也就是下一次判断的时候,当前点应该转移到下一个点上,利用栈的特点,我们每一次看栈头的时候,看到的都是最新的点,可以满足需求
③由于所有的节点都会从出栈口经过,所以所有的判断语句写在这里是比较方便的,当然,写在所有的进栈口也不是不行,这一点可以类比queue实现bfs,bfs中的进队口和出队口都可以写判断语句,但是,不论是dfs还是bfs,都是在出栈口写比较方便
#include<stack> vector<int> G[num+10]; int visited[num+10]; stack<int> s; void dfs(int start){ s.push(start); visited[start]=1; while(!s.empty()){ int vertax=s.top(); bool is_push=false; for(int i=0;i<G[vertax].size();i++){ int point=G[vertax][i]; if(visited[point]==0){ is_push=true; s.push(point); visited[point]=1; break; } } if(!is_push){ /* 所有节点的出栈口,在这里进行性质判断比较好,但这是一个逆序 */ s.pop(); } } } int main(){ memset(visited,0,sizeof(visited)); for(int i=1;i<=num;i++){ if(visited[i]!=0)continue; dfs(i); } return 0; }
3.时间复杂度
邻接表的时间复杂度:O(|V|+|E|)
邻接矩阵的复杂度:O(|V|^2)