[Algorithms] Graph Traversal (BFS and DFS)

本文详细介绍了图这一重要数据结构的两种遍历方法:广度优先搜索(BFS)与深度优先搜索(DFS),并提供了使用C++实现这两种算法的具体代码示例。文中还解释了如何用邻接表和邻接矩阵表示图,并展示了这些表示方法如何应用到实际的图遍历过程中。

Graph is an important data structure and has many important applications. Moreover, grach traversal is key to many graph algorithms. There are two systematic ways to traverse a graph, breadth-first search (BFS) and depth-frist search (DFS). 

Before focusing on graph traversal, we first determine how to represent a graph. In fact, there are mainly two ways to represent a graph, either using adjacency lists or adjacency matrix.

An adjacency list is an array of lists. Each list corresponds to a node of the graph and stores the neighbors of that node.

For example, for the (undirected) graph above, its representation using adjacency lists can be:

0: 1 -> 3 -> NULL

1: 0 -> 2 -> NULL

2: 1 -> NULL

3: 0 -> 4 -> 5 -> NULL

4: 3 -> 5 -> 6 -> NULL

5: 3 -> 4 -> 6 -> 7 -> NULL

6: 4 -> 5 -> 7 -> NULL

7: 5 -> 6 -> NULL

An adjacency matrix is a matrix of size m by m (m is the number of nodes in the graph) and the (i, j)-the element of the matrix represents the edge from node i to node j.

For the same graph above, its representation using adjacency matrix is:

0 1 0 1 0 0 0 0

1 0 1 0 0 0 0 0 

0 1 0 0 0 0 0 0 

1 0 0 0 1 1 0 0 

0 0 0 1 0 1 1 0

0 0 0 1 1 0 1 1

0 0 0 0 1 1 0 1 

0 0 0 0 0 1 1 0

In this passage, we use adjacency lists to represent a graph. Specifically, we define the node of the graph to be the following structure:

1 struct GraphNode {
2     int label;
3     vector<GraphNode*> neighbors;
4     GraphNode(int _label) : label(_label) {}
5 };

Now let's move on to BFS and DFS.

As suggested by their names, BFS will first visit the current node, then its neighbors, then the non-visited neighbors of its neighbors... and so on in a breadth-first manner while DFS will try to move as far as possible from the current node and backtrack when it cannot move forward any more (all the neighbors of the current node has been visited).

The implementation of BFS requries the use of the queue data structure while the implementation of DFS can be done in a recursive manner.

For more details on BFS and DFS, you may refer to Introduction to Algorithms or these two nice videos: BFS video and DFS video.

In my implementation, BFS starts from a single node and visits all the nodes reachable from it and returns a sequence of visited nodes. However, DFS will try to start from every non-visited node in the graph and starts from that node and obtains a sequence of visited nodes for each starting node. Consequently, the function bfs returns a vector<GraphNode*> while the function dfs returns a vector<vector<GraphNode*> >.

I also implement a function read_graph to input the graph manually. For the above graph, you first need to input its number of nodes and number of edges. Then you will input each of its edge in the form of "0 1" (edge from node 0 to node 1).

The final code is as follows.

  1 #include <iostream>
  2 #include <vector>
  3 #include <queue>
  4 #include <unordered_set>
  5 
  6 using namespace std;
  7 
  8 struct GraphNode {
  9     int label;
 10     vector<GraphNode*> neighbors;
 11     GraphNode(int _label) : label(_label) {}
 12 };
 13 
 14 vector<GraphNode*> read_graph(void) {
 15     int num_nodes, num_edges;
 16     scanf("%d %d", &num_nodes, &num_edges);
 17     vector<GraphNode*> graph(num_nodes);
 18     for (int i = 0; i < num_nodes; i++)
 19         graph[i] = new GraphNode(i);
 20     int node, neigh;
 21     for (int i = 0; i < num_edges; i++) {
 22         scanf("%d %d", &node, &neigh);
 23         graph[node] -> neighbors.push_back(graph[neigh]);
 24         graph[neigh] -> neighbors.push_back(graph[node]);
 25     }
 26     return graph;
 27 }
 28 
 29 vector<GraphNode*> bfs(vector<GraphNode*>& graph, GraphNode* start) {
 30     vector<GraphNode*> nodes;
 31     queue<GraphNode*> toVisit;
 32     unordered_set<GraphNode*> visited;
 33     toVisit.push(start);
 34     visited.insert(start);
 35     while (!toVisit.empty()) {
 36         GraphNode* cur = toVisit.front();
 37         toVisit.pop();
 38         nodes.push_back(cur);
 39         for (GraphNode* neigh : cur -> neighbors) {
 40             if (visited.find(neigh) == visited.end()) {
 41                 toVisit.push(neigh);
 42                 visited.insert(neigh);
 43             }
 44         }
 45     }
 46     return nodes;
 47 }
 48 
 49 bool visitAllNeighbors(GraphNode* node, unordered_set<GraphNode*>& visited) {
 50     for (GraphNode* n : node -> neighbors)
 51         if (visited.find(n) == visited.end())
 52             return false;
 53     return true;
 54 }
 55 
 56 void dfs_visit(vector<GraphNode*>& graph, GraphNode* node, \
 57                unordered_set<GraphNode*>& visited, vector<GraphNode*>& tree, \
 58                vector<vector<GraphNode*> >& forest) {
 59     visited.insert(node);
 60     tree.push_back(node);
 61    if (visitAllNeighbors(node, visited)) {
 62         forest.push_back(tree);
 63         tree.clear();
 64         return;
 65     }    
 66     for (GraphNode* neigh : node -> neighbors)
 67         if (visited.find(neigh) == visited.end())
 68             dfs_visit(graph, neigh, visited, tree, forest);
 69 }
 70 
 71 vector<vector<GraphNode*> > dfs(vector<GraphNode*>& graph) {
 72     vector<GraphNode*> tree;
 73     vector<vector<GraphNode*> > forest;
 74     unordered_set<GraphNode*> visited;
 75     for (GraphNode* node : graph)
 76         if (visited.find(node) == visited.end())
 77             dfs_visit(graph, node, visited, tree, forest);
 78     return forest;
 79 }
 80 
 81 void graph_test(void) {
 82     vector<GraphNode*> graph = read_graph();
 83     // BFS
 84     printf("BFS:\n");
 85     vector<GraphNode*> nodes = bfs(graph, graph[0]);
 86     for (GraphNode* node : nodes)
 87         printf("%d ", node -> label);
 88     printf("\n");
 89     // DFS
 90     printf("DFS:\n");
 91     vector<vector<GraphNode*> > forest = dfs(graph);
 92     for (vector<GraphNode*> tree : forest) {
 93         for (GraphNode* node : tree)
 94             printf("%d ", node -> label);
 95         printf("\n");
 96     }
 97 }
 98 
 99 int main(void) {
100     graph_test();
101     system("pause");
102     return 0;
103 }

If you input the above graph to it as follows (note that you only need to input each edge exactly once):

8 10
0 1
0 3
1 2
3 4
3 5
4 5
4 6
5 6
5 7
6 7

The output will be as follows:

1 BFS:
2 0 1 3 2 4 5 6 7
3 DFS:
4 0 1 2
5 3 4 5 6 7

You may check it manually and convince yourself of its correctness :)

转载于:https://www.cnblogs.com/jcliBlogger/p/4603410.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值