深入浅出BFS/DFS(3)—孤立点的处理

12 篇文章 0 订阅
9 篇文章 0 订阅

图上的BFS/DFS方法——孤立点的处理:

参考:
1. 邻接矩阵
直接开一个N×N的二维数组E,然后 E[i][j] 为1的时候表示 i 和 j 之间有一条边,0的时候就没有。
这样很方便简单,但是也有缺点,

1 首先是效率问题,超过1000个点一般不管是空间还是时间都不允许了。
2 然后就是如果两个顶点之间有两条边的话,就没法表示了。

所以现在一般很少用这种方法,当然不排除某些时候还是可以使用的。
2. 邻接表
使用链表的方式保存一个顶点的所有相连接的边,每个顶点表示为这个链表的起始的节点。
当然,每个顶点都要维护一个链表,这个时候可以使用 vector 数组比较方便。

3 使用邻接表建立无向图的一个例子:

// A simple representation of graph using STL 
#include<bits/stdc++.h> 
using namespace std; 
 
// A utility function to add an edge in an 
// undirected graph. 
void addEdge(vector<int> adj[], int u, int v) { 
	adj[u].push_back(v); 
	adj[v].push_back(u); 
} 
 
// A utility function to print the adjacency list 
// representation of graph 
void printGraph(vector<int> adj[], int V) { 
	for (int v = 0; v < V; ++v) { 
		cout << "\n Adjacency list of vertex "
			<< v << "\n head "; 
		for (auto x : adj[v]) 
			cout << "-> " << x; 
		printf("\n"); 
	} 
} 
 
// Driver code 
int main() { 
	int V = 5; 
	vector<int> adj[V]; 
	addEdge(adj, 0, 1); 
	addEdge(adj, 0, 4); 
	addEdge(adj, 1, 2); 
	addEdge(adj, 1, 3); 
	addEdge(adj, 1, 4); 
	addEdge(adj, 2, 3); 
	addEdge(adj, 3, 4); 
	printGraph(adj, V); 
	return 0; 
} 

DFS
需要一个,因为每次都是搜到之后不停的往下搜,符合先进先出。但是一般来说不用栈,而是直接通过函数的递归就行。
由于可能存在,所以需要设置 bool 类型的数组,防止有的节点会重复遍历,导致进入无休止的循环遍历状态。

// C++ program to print DFS traversal from 
// a given vertex in a given graph 
#include<iostream> 
#include<list> 
using namespace std; 
 
// Graph class represents a directed graph 
// using adjacency list representation 
class Graph { 
	int V; // No. of vertices 
 
	// Pointer to an array containing 
	// adjacency lists 
	list<int> *adj; 
 
	// A recursive function used by DFS 
	void DFSUtil(int v, bool visited[]); 
public: 
	Graph(int V); // Constructor 
 
	// function to add an edge to graph 
	void addEdge(int v, int w); 
 
	// DFS traversal of the vertices 
	// reachable from v 
	void DFS(int v); 
}; 
 
Graph::Graph(int V) { 
	this->V = V; 
	adj = new list<int>[V]; 
} 
 
void Graph::addEdge(int v, int w) { 
	adj[v].push_back(w); // Add w to v’s list. 
} 
 
void Graph::DFSUtil(int v, bool visited[]) { 
	// Mark the current node as visited and 
	// print it 
	visited[v] = true; 
	cout << v << " "; 
	// Recur for all the vertices adjacent 
	// to this vertex 
	list<int>::iterator i; 
	for (i = adj[v].begin(); i != adj[v].end(); ++i) 
		if (!visited[*i]) 
			DFSUtil(*i, visited); 
} 
// DFS traversal of the vertices reachable from v. 
// It uses recursive DFSUtil() 
void Graph::DFS(int v) { 
	// Mark all the vertices as not visited 
	bool *visited = new bool[V]; 
	for (int i = 0; i < V; i++) 
		visited[i] = false; 
 
	// Call the recursive helper function 
	// to print DFS traversal 
	DFSUtil(v, visited); 
} 
 
int main() { 
	// Create a graph given in the above diagram 
	Graph g(4); 
	g.addEdge(0, 1); 
	g.addEdge(0, 2); 
	g.addEdge(1, 2); 
	g.addEdge(2, 0); 
	g.addEdge(2, 3); 
	g.addEdge(3, 3); 
 
	cout << "Following is Depth First Traversal"
			" (starting from vertex 2) \n"; 
	g.DFS(2); 
	return 0; 
} 

访问存在孤立点的图
上面是从一个指定的源点DFS访问节点,但是如果图中有孤立的点,则不能保证图中所有的点都能被访问到。
所以,改变遍历的方式,改为不指定源点,将每一个节点都设置为源点,只要在从源点开始DFS遍历前,判断一下是否已经访问过就可以了。代码改动如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <list>
#include <bits/stdc++.h>
using namespace std;
 
// C++ program to print DFS traversal from
// a given vertex in a given graph
 
// Graph class represents a directed graph
// using adjacency list representation
class Graph{
	int V; // No. of vertices
 
	// Pointer to an array containing
	// adjacency lists
	list<int> *adj;
 
	// A recursive function used by DFS
	void DFSUtil(int v, bool visited[]);
public:
	Graph(int V); // Constructor
 
	// function to add an edge to graph
	void addEdge(int v, int w);
 
	// DFS traversal of the vertices
	// reachable from v
	void DFS(); // ---------------------------
};
 
Graph::Graph(int V){
	this->V = V;
	adj = new list<int>[V];
}
 
void Graph::addEdge(int v, int w){
	adj[v].push_back(w); // Add w to v’s list.
}
 
void Graph::DFSUtil(int v, bool visited[]){
	// Mark the current node as visited and
	// print it
	visited[v] = true;
	cout << v << " ";
 
	// Recur for all the vertices adjacent
	// to this vertex
	list<int>::iterator i;
	for (i = adj[v].begin(); i != adj[v].end(); ++i)
		if (!visited[*i])
			DFSUtil(*i, visited);
}
 
// DFS traversal of the vertices reachable from v.
// It uses recursive DFSUtil()
void Graph::DFS(){
	// Mark all the vertices as not visited
	bool *visited = new bool[V];
	for (int i = 0; i < V; i++)
		visited[i] = false;
 
	// Call the recursive helper function
	// to print DFS traversal
	for(int i=0; i<V; i++){
	    if(!visited[i])
            DFSUtil(i, visited); // --------------------
	}
 
}
 
int main(){
	// Create a graph given in the above diagram
	Graph g(7);
	g.addEdge(0, 1);
	g.addEdge(0, 2);
	g.addEdge(1, 2);
	g.addEdge(2, 0);
	g.addEdge(2, 3);
	g.addEdge(3, 3);
 
	cout << "Following is Depth First Traversal"
			" (starting from vertex 2) \n";
	g.DFS(); // ---------------------
	return 0;
}

下面也是一种写法:

bool vis[110];
int N;
void DFS(int u){
    int len;
    vis[u]=1;
    len=E[u].size();
 
    for(int i=0;i<len;++i)
        if(vis[E[u][i]]==0)
            DFS(E[u][i]);
}

DFS的非递归写法

#include <iostream>
#include <cstdio>
#include <cstring>
#include <list>
#include <bits/stdc++.h>
using namespace std;
 
// C++ program to print DFS traversal from
// a given vertex in a given graph
 
// Graph class represents a directed graph
// using adjacency list representation
class Graph{
	int V; // No. of vertices
 
	// Pointer to an array containing
	// adjacency lists
	list<int> *adj;
    stack<int>st;
 
	// A recursive function used by DFS
	void DFSUtil(int v, bool visited[], stack<int>st);
public:
	Graph(int V); // Constructor
 
	// function to add an edge to graph
	void addEdge(int v, int w);
 
	// DFS traversal of the vertices
	// reachable from v
	void DFS();
};
 
Graph::Graph(int V){
	this->V = V;
	adj = new list<int>[V];
}
 
void Graph::addEdge(int v, int w){
	adj[v].push_back(w); // Add w to v¡¯s list.
}
 
void Graph::DFSUtil(int v, bool visited[], stack<int> st){
	// Mark the current node as visited and
	// print it
	visited[v] = true; // 遍历每个源点,如果没有访问,就打印输出,并入栈
	cout << v << " ";
	st.push(v);
	while(!st.empty()){ // 如果栈不为空
        int head = st.top();
        list<int>::iterator it = adj[head].begin();
        bool flag = false; // 设置标记,如果当前节点没有邻接点或者邻接点都已经被访问过,则将当前栈中的一个元素出队
        for(it = adj[head].begin(); it!=adj[head].end(); it++) { // 开始遍历当前节点的邻接点
            if(!visited[*it]) { //  如果邻接点没有访问过
                cout<< *it <<" ";
                st.push(*it);
                visited[*it] = true; //设置邻接点已经访问过
                flag = true; // 设置标记为 true,代表当前节点存在一个没有被访问过的邻接点
                break;
            }
        }
        if(flag == false) { // 如果当前节点没有邻接点或者邻接点都已经被访问过,则栈不为空时出栈一个元素。
            if(!st.empty())
                st.pop();
        }
    }
 
}
 
 
void Graph::DFS(){
 
    bool *visited = new bool[V];
    for(int i=0; i<V; i++)
        visited[i] = false;
 
    for(int i=0; i<V; i++) { //依次以每个结点为源点
        if(!visited[i])
            DFSUtil(i, visited, st);
    }
}
int main(){
	// Create a graph given in the above diagram
	Graph g(10);
	g.addEdge(0, 1);
	g.addEdge(3, 3);
	g.addEdge(2, 4);
	g.addEdge(2, 5);
 
	cout << "Following is Depth First Traversal"
			" (starting from vertex 2) \n";
	g.DFS();
	return 0;
}

BFS
广度优先搜索,顾名思义,就是先搜索完同一层次的所有节点,然后到下一层,继续搜索下一层的所有节点。以此类推,搜索完所有的节点。
这种遍历的方式类似于遍历一个队列,如同我把每层的节点从左到右放入队列中,所有的层从上到下遍历完。所以,我们可以借助辅助队列,来进行广度优先搜索。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <list>
#include <bits/stdc++.h>
using namespace std;
 
 
// 新建一个 Graph 类,使用邻接表表示一个有向图,
class Graph{
private:
    int V; // 表示有向图中节点的数量
    list<int> *adj; // 使用STL中的 list 容器,定义链表数组,数组中的每一个元素表示一个链表
public:
    Graph(int V); //构造函数初始化
    void addEdge(int v, int w); // 向图添加一条边
    void BFS(int s); //从节点 s 开始,BFS 遍历图。
 
};
 
Graph::Graph(int V)
{
    this->V = V;
    adj = new list<int>[V];
}
 
void Graph::addEdge(int v, int w){
    adj[v].push_back(w);// 把 w 添加到v的链表中。
}
 
void Graph::BFS(int s){
 
    bool *visit = new bool[V];
    for(int i=0; i<V; i++)
        visit[i] = false;
 
    list<int>queue;// list 是一个双向链表,所以在两头都可以执行插入删除操作,
                   // 所以直接使用 list 来模拟队列
    visit[s] = true; //标记当前顶点已经被访问标记过,并入队列
    queue.push_back(s);
 
    list<int>::iterator it; // 初始化迭代器,用于遍历容器,这里是用作遍历当前节点的邻接点
 
    while(!queue.empty()){
        s = queue.front();
        cout << s << " ";
        queue.pop_front();
 
        for(it = adj[s].begin(); it!=adj[s].end(); it++){
            if(!visit[*it]) // 如果已经访问过,则继续遍历下一个没有访问过得邻接点
            {
                visit[*it] = true; //没有访问过,则先标记已经访问过了,防止重复遍历
                queue.push_back(*it);
            }
        }
    }
}
int main(){
    Graph g(4);
    g.addEdge(0, 1);
    g.addEdge(0, 2);
    g.addEdge(1, 2);
    g.addEdge(2, 0);
    g.addEdge(2, 3);
    g.addEdge(3, 3);
    cout << "Following is Breadth First Traversal "
         << "(starting from vertex 2) \n";
    g.BFS(2);//从 2 开始进行BFS遍历
    return 0;
}

上面的写法如果是在图中存在孤立点的时候,也会不能遍历到,所以,应该以每个结点为源点,进行BFS遍历,具体的写法可以参看上面 DFS中的访问图中存在孤立点的写法

bool vis[110];            // 记录已经走过的点,防止重复访问。
 
void BFS(int root,int N)        // N个点的图,从root点开始搜索。
{
    queue <int> que;
 
    memset(vis,0,sizeof(vis));    // 初始化。
    vis[root]=1;
    que.push(root);
 
    int u,len;
 
    while(!que.empty()){
        u=que.front();
        que.pop();
 
        len=E[u].size();
        for(int i=0;i<len;++i)        // 找到和u相连的所有点,存在一个vector里面。
            if(vis[E[u][i]]==0){
                vis[E[u][i]]=1;
                que.push(E[u][i]);
            }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值