BFS 和 DFS

文中的代码和部分图参考了 Graph and its representations

目录

图的表示的方法:

DFS,参考:Depth First Search or DFS for a Graph

 访问存在孤立点的图

下面也是一种写法:参考 算法录 之 BFS和DFS

DFS的非递归写法

BFS(广度优先搜索)参考:Breadth First Search or BFS for a Graph

对上面的图进行BFS遍历的话:结果为 2,0,3,1。

访问存在孤立点的图

下面的写法类似:参考 算法录 之 BFS和DFS

 应用:

参考文献


  • 图的表示的方法:

  • 邻接矩阵

直接开一个N×N的二维数组E,然后 E[i][j] 为1的时候表示 i 和 j 之间有一条边,0的时候就没有。

      这样很方便简单,但是也有缺点,

                  1、首先是效率问题,超过1000个点一般不管是空间还是时间都不允许了。

                  2、然后就是如果两个顶点之间有两条边的话,就没法表示了。。。

      所以现在一般很少用这种方法,当然不排除某些时候还是可以使用的。

Adjacency Matrix Representation
上面的无向图的邻接矩阵的表示方法
int E[5][5];

E[1][2]=1;
E[4][2]=0;

 

  • 邻接表

使用链表的方式保存一个顶点的所有相连接的边,每个顶点表示为这个链表的起始的根节点。

当然,每个顶点都要维护一个链表,这个时候可以使用 vector 数组比较方便。

Adjacency List Representation of Graph
上面的无向图的邻接表的表示方法
vector <int> E[5];

E[4].push_back(1) // 有一条从4到1的边。
  •  使用邻接表建立无向图的一个例子:
// 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; 
} 

 

先上图:需要一个栈,因为每次都是搜到之后不停的往下搜,符合先进先出。但是一般来说不用栈,而是直接通过函数的递归就行了。

如图所示,由于存在环,所以需要设置 bool 类型的数组,防止有的节点会重复遍历,导致进入无休止的循环遍历状态。

先从2开始搜索,然后0与2连接,所以搜索0,再从0开始搜索,由于有一条指向2的路线,但是2一开始就已经访问过了,所以选择与0相连接的另一条没有被访问过节点1。此时从1开始搜索,1只有一条指向2的路线,但是2已经遍历过,所以回退到上一个连接点0,与0向连接的节点都已经被访问过了,所以再回退到2,此时还有与2连接的节点3没有访问过,此时访问3,至此,DFS遍历结束。 

 

// 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; 
} 

 

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==
  •  访问存在孤立点的图

上面是从一个指定的源点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]);
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

  • 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;
}


 

广度优先搜索,顾名思义,就是先搜索完同一层次的所有节点,然后到下一层,继续搜索下一层的所有节点。以此类推,搜索完所有的节点。

这种遍历的方式类似于遍历一个队列,如同我把每层的节点从左到右放入队列中,所有的层从上到下遍历完。所以,我们可以借助辅助队列,来进行广度优先搜索。

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

对上面的图进行BFS遍历的话:结果为 2,0,3,1。

#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;
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==
  • 访问存在孤立点的图

上面的写法如果是在图中存在孤立点的时候,也会不能遍历到,所以,应该以每个结点为源点,进行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]);
            }
    }
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

  •  应用:

  1. BFS算法求解单源最短路劲问题
  2.  
  • 参考文献

  1. Graph and its representations
  2. Breadth First Search or BFS for a Graph
  3. Depth First Search or DFS for a Graph
  4. 算法录 之 BFS和DFS
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值