图算法(1)-广度优先遍历
图的广度优先遍历其实并不难,当边的权重相同时,由图的广度优先遍历可以得到其余节点到源节点的最短路径。我使用了邻接表实现了这个算法。
首先来看看图的数据结构的定义:
1 struct Edge 2 { 3 int end; 4 }; 5 6 enum Color {WHITE, GRAY, BLACK}; 7 8 struct Node 9 { 10 std::string value; 11 std::vector<Edge> edges; 12 int d; 13 int p; 14 Color color; 15 }; 16 17 struct Graph 18 { 19 std::vector<Node> nodes; 20 std::map<std::string, int> nodemap; 21 22 void InsertNode(std::string value) 23 { 24 Node n; 25 n.value = value; 26 nodes.push_back(n); 27 nodemap[value] = nodes.size() - 1; 28 } 29 30 void InsertEdge(int begin, int end) 31 { 32 Edge e = { end }; 33 nodes[begin].edges.push_back(e); 34 } 35 36 void InsertEdge(std::string beginv, std::string endv) 37 { 38 int begin = nodemap[beginv]; 39 int end = nodemap[endv]; 40 InsertEdge(begin, end); 41 } 42 43 void InsertUndirEdge(int begin, int end) 44 { 45 InsertEdge(begin, end); 46 InsertEdge(end, begin); 47 } 48 49 void InsertUndirEdge(std::string beginv, std::string endv) 50 { 51 int begin = nodemap[beginv]; 52 int end = nodemap[endv]; 53 InsertUndirEdge(begin, end); 54 } 55 };
在这里,Edge只记录了end节点,之所以单独封装成一个结构,一是语义上更加直观,二是以后想要扩充时会比较方便(比如需要扩充为带权重的图时,在Edge里加一个权重项就可以了,已有算法不用改变)。Node里value代表节点的语义值,比如我们常常面对实际问题的时候,并不是直接给的“节点1”,给的可能是“节点a”,记录这个语义值可以方便最后输出计算结果。Node里还有一个edges记录以该node为begin的边,同时要注意的时,Node里还加入了d,p,color。d用来记录该节点到源节点的距离,p记录该节点到源节点最短路径的父节点,color用于记录在遍历过程中,节点是否已经遍历到(WHITE没有遍历到,GRAY遍历到,但是还有与之相连的节点没有,BLACK表示遍历完成)。Graph主要是存储了一个邻接表nodes,其余的函数都是为了方便建图。(采取先插入所有节点,再插入边的方式)。其中插入边的时候,可以按照节点的语义值插入,这时候我们先搜索语义值对应的节点编号,然后再插入对应边信息。
有了以上的数据结构,我们的广度优先遍历算法就变得非常简单明了了:
1 void BFS(Graph& g, int s) 2 { 3 for (size_t i = 0; i < g.nodes.size(); ++i) 4 { 5 g.nodes[i].d = -1; 6 g.nodes[i].p = -1; 7 g.nodes[i].color = WHITE; 8 } 9 g.nodes[s].d = 0; 10 g.nodes[s].p = -1; 11 g.nodes[s].color = GRAY; 12 queue<int> q; 13 q.push(s); 14 while (!q.empty()) 15 { 16 int u = q.front(); q.pop(); 17 for (size_t i = 0; i < g.nodes[u].edges.size(); ++i) 18 { 19 int v = g.nodes[u].edges[i].end; 20 if (g.nodes[v].color == WHITE) 21 { 22 g.nodes[v].d = g.nodes[u].d + 1; 23 g.nodes[v].p = u; 24 g.nodes[v].color = GRAY; 25 q.push(v); 26 } 27 } 28 g.nodes[u].color = BLACK; 29 } 30 }