每日一题31:图的遍历
来源:互联网 发布:淘宝店可以用手机开么 编辑:程序博客网 时间:2024/06/08 07:59
算法概述
图的遍历是指访问图中每个节点一次。图的遍历方式主要有两种,一种是深度优先,即能走多远就先走多远的遍历方式,这就意味着,对于每个节点的遍历完后,下一个访问的节点应该是他的邻接点,而不是兄弟节点。另一种方式是深度优先的方式,这是一种分层遍历,对于没一个节点访问完后,就访问它的兄弟节点,而不是优先考虑邻接顶点。深度优先算法使用递归实现比较直观,而广度优先遍历则需要一个队列辅助,和分层遍历一棵二叉树的算法是一样一样的。
算法实现
#ifndef _GRAPHTRAVERSE_H_#define _GRAPHTRAVERSE_H_#include "../include/DirectedWeightGraph.h"#include <queue>using namespace MyDataStructure;using namespace std;namespace MyTools{ //遍历的流程是一样的,所以将遍历方式写成一个仿函数,函数指针 //太难看了 template<typename Value, typename Weight, typename Visitor> struct bfs { void operator()(DirectedWeightGraph<Value, Weight>& Graph, int v, Vector<bool>& is_visited,Visitor vf) { typedef DirectedWeightGraph<Value, Weight>::VerticePtr VerticePtr; typedef DirectedWeightGraph<Value, Weight>::EdgePtr EdgePtr; if (is_visited[v] == true) return; queue<int> q; q.push(v); while (q.empty() != true) { int v1 = q.front(); if (is_visited[v1] != true && Graph.IsVerticeContianed(v1)) { VerticePtr v_ptr = Graph.GetVertice(v1); vf(v_ptr->value); is_visited[v1] = true; EdgePtr e = v_ptr->adj; while (e != nullptr) { if (is_visited[e->dst] != true) { q.push(e->dst); } e = e->next; } } q.pop(); } } }; template<typename Value, typename Weight, typename Visitor> struct dfs { void operator()(DirectedWeightGraph<Value, Weight>& Graph, int v, Vector<bool>& is_visited,Visitor vf) { typedef DirectedWeightGraph<Value, Weight>::VerticePtr VerticePtr; typedef DirectedWeightGraph<Value, Weight>::EdgePtr EdgePtr; if (Graph.IsVerticeContianed(v) && is_visited[v] != true) { VerticePtr v_ptr = Graph.GetVertice(v); vf(v_ptr->value); is_visited[v] = true; EdgePtr e = v_ptr->adj; while (e != nullptr) { if (is_visited[e->dst] != true) { operator()(Graph,e->dst, is_visited, vf); } e = e->next; } } } }; //遍历流程,可配置遍历方式、遍历的起点和对每个节点的访问操作 template<typename Value, typename Weight,typename TraverseMethod, typename Visitor> bool Traverse(DirectedWeightGraph<Value, Weight>& Graph,int start,TraverseMethod tm, Visitor vf) { if (Graph.IsVerticeContianed(start) == true) { int size = Graph.GetVerticeSize(); Vector<bool> visited(size); for (int i = 0; i < size; ++i) { visited[i] = false; } //如果图是由多个连通分量构成的,这个循环确保每个分量 //的入口不会被错过 for (int i = 0; i < size; ++i) { //起点可能在存储结构的中间,所以用模运算 //让遍历从中间能走到开头 int v = start++ % size; if (visited[v] == false) { tm(Graph,v, visited, vf); cout << endl; } } return true; } return false; } //提供给外部使用的比较方便的接口 template<typename Value, typename Weight,typename Visitor> bool BFS(DirectedWeightGraph<Value,Weight>& Graph, int start,Visitor vf) { bfs<Value, Weight, Visitor> b; return Traverse(Graph, start, b, vf); } template<typename Value, typename Weight,typename Visitor> bool DFS(DirectedWeightGraph<Value, Weight>& Graph, int start, Visitor vf) { dfs<Value, Weight, Visitor> d; return Traverse(Graph, start, d, vf); }}#endif
测试代码:
// GraphTest.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include "../include/DirectedWeightGraph.h"#include "../Tool/MinGenTree.h"#include "../Tool/ShortestPath.h"#include "../Tool/TopologicalSort.h"#include "../Tool/GraphTraverse.h"#include <string>#include <iostream>#include <fstream>using namespace MyDataStructure;using namespace MyTools;using namespace std;bool LoadVertice(string filename, Vector<string>& citynames){ ifstream input; citynames.Clear(); input.open(filename); string city; int no; while (input>>no>>city) { citynames.PushBack(city); } input.close(); return true;}bool LoadEdge(string filename, Vector<int>& srcs,Vector<int>& dsts,Vector<float>& weights){ ifstream input; srcs.Clear(); dsts.Clear(); weights.Clear(); input.open(filename); int src, dst; float weight; while (input>>src>>dst>>weight) { srcs.PushBack(src); dsts.PushBack(dst); weights.PushBack(weight); } input.close(); return true;}//节点访问函数void print1(const string& s){ cout << "-->" << s;}//以仿函数的形式访问节点struct Vis{ Vis(string s) { this->s = s; } void operator()(const string& value) { cout <<s + "-->" << value; }private: string s;};int _tmain(int argc, _TCHAR* argv[]){ Vector<int> srcs,dsts; Vector<string> citynames; Vector<float> weights; LoadVertice("vertice - 副本.txt", citynames); LoadEdge("edge - 副本.txt", srcs, dsts, weights); DirectedWeightGraph<string, float> WG(citynames, srcs, dsts, weights); int verticecount = citynames.Size(), edgecount = weights.Size(); cout<<"广度优先:"<<endl; BFS(WG, 0, Vis("***")); cout<<"深度优先:"<<endl; DFS(WG, 0, print1); return 0;}
程序运行结果示例:
输入图
边的文件存储形式
顶点的文件存储形式
运行结果:
总结
理解里遍历方式的运行原理之后编码还是比较容易的,但是实现宽度优先遍历的时候,把栈拿掉也可以遍历所有的节点,但是没有分层访问的效果,那么也就不是宽度优先遍历所定义的运行方式了。另外在实现过程中,我总结了一下仿函数至少具有的一下优点:
- 形式更简单,所以写出来的接口更容易阅读
- 可以拥有自己的状态,并带着状态传递,还可以在运行过程中改变状态
- 兼容函数指针,可以将函数指针当做仿函数作为接口实参
以上三点都在本博客的测试代码中有所体现。
1 0
- 每日一题31:图的遍历
- 每日一题之二叉树的层序遍历
- 实现一颗二叉树的层序遍历【每日一题】
- 【每日一题-9】层序遍历与N!后的0的个数
- 每日一题之实现二叉树的前序/中序/后序非递归遍历
- 【每日一题-15】二叉树非递归遍历&求两个集合的差集
- 每日一题(72) - 分层遍历二叉树
- 二叉树三种遍历非递归实现【每日一题】
- 每日一题之由前序遍历和中序遍历重建二叉树
- 每日一算法:骑士遍历问题
- 每日一题(31) - 二叉树的镜像
- 每日一题 No.31 学习ctype.h的用法
- 每日一题28:图的基本操作
- 图的遍历一
- 每日一题(54) - 扑克牌的顺序
- 每日一题(66) - 字符串的排列
- 每日一题 - 剩下的数字?
- oracle 每日一题-游标的参数
- 【swift】15-0530 闭包
- 解决Android Studio 和 Android SDK Manager 无法在线更新的问题.
- IOS逆向工程
- 社説 20150530 安保法案審議 専守防衛の本質は変わらない
- TCP/IP、Http、Socket的区别
- 每日一题31:图的遍历
- shell-mysql
- 连续时间马尔科夫过程
- csu 1060 Nearest Sequence
- Page、Request、Session、Application
- 安卓的进程与线程及其相关代码实现
- linux(centos7)搭建nginx服务器
- Codeforces Round #305 (Div. 2) C. Mike and Frog
- arduino最版!!!!1.6.4官方正版