这篇文章用来复习使用BFS(Breadth First Search)和DFS(Depth First Search) 并解决一个在旅游时遇到的问题.

关于图的邻接表存储与邻接矩阵的存储,各有优缺点.但是邻接矩阵继承了数组的优点--在索引方面速度相比链表要快的多.由于以前我实现过邻接矩阵的存储方式,这次就用邻接表的方式复习吧.



图的邻接表存储简单示意


0.前提

  • 类中的api以及属性
  1. class CGraph
  2. {
  3. public:
  4. CGraph(void);
  5. ~CGraph(void);
  6. // 根据顶点的数量初始化邻接表.
  7. void initVertex(int _vertexNum);
  8. // 添加一条边至图中.
  9. int addEdge(edge_s _edge);
  10. // 使用bfs遍历
  11. void traversal_bfs(int _vertex);
  12. // 使用dfs遍历
  13. void traversal_dfs(int _vertex);
  14. // 显示图的基本存储信息
  15. void printGraphVertex();
  16. private:
  17. // 顶点数量
  18. int vertexNum;
  19. // 边数量
  20. int edgeNum;
  21. // 顶点数组
  22. Node_s * vertexPtr;
  23. // 记录顶点被访问状态
  24. VISITEDSTATE * visited;
  25. private:
  26. // 更新访问状态
  27. void updateVisited();
  28. // 用于dfs遍历
  29. void dfs(int _vertex);
  30. // 改变顶点被访问状态
  31. void setVertexState(int _vertex,VISITEDSTATE _state);
  32. // 获取顶点被访问状态
  33. VISITEDSTATE getVertexState(int _vertex);
  34. };
  • 节点的定义与其初始化
  1. enum VISITEDSTATE{
  2. VISITED = 0,
  3. NOVISIT = 1
  4. };
  5. // 顶点的定义
  6. struct Node_s {
  7. Node_s():verIndex(0),weight(0),NEXT(nullptr){}
  8. // 顶点的编号
  9. int verIndex;
  10. // 权重
  11. int weight;
  12. Node_s * NEXT;
  13. };
  14. // 边的定义
  15. struct edge_s {
  16. edge_s() :start(0),end(0),weight(0){}
  17. int start;
  18. int end;
  19. int weight;
  20. };
  21. //节点的初始化,edgeNum是边的数量.
  22. void CGraph::initVertex(int _vertexNum){
  23. vertexNum = _vertexNum;
  24. edgeNum = 0;
  25. vertexPtr = new Node_s[vertexNum]();
  26. // // 记录顶点被访问状态的数组
  27. visited = new VISITEDSTATE[vertexNum]();
  28. for (int i = 0;i < vertexNum;i++)
  29. {
  30. visited[i] = NOVISIT;
  31. vertexPtr[i].verIndex = i;
  32. vertexPtr[i].weight = 0;
  33. vertexPtr[i].NEXT = nullptr;
  34. }
  35. }
  • 边的加入(图的构建)
  1. int CGraph::addEdge(edge_s _edge){
  2. int start = _edge.start;
  3. int end = _edge.end;
  4. if (start >= vertexNum || end >= vertexNum)
  5. {
  6. std::cout <<"此顶点不存在于邻接表中"<<std::endl;
  7. return -1;
  8. }
  9. Node_s * new_node = new Node_s;
  10. if (!new_node)
  11. {
  12. return -1;
  13. }
  14. // 头插入法
  15. new_node->verIndex = end;
  16. new_node->weight = _edge.weight;
  17. new_node->NEXT = vertexPtr[start].NEXT;
  18. vertexPtr[start].NEXT = new_node;
  19. edgeNum ++;
  20. return 0;
  21. }

1.BFS

  非常类似于树的层序遍历

  1. void CGraph::traversal_bfs(int _vertex){
  2. updateVisited();
  3. std::queue<Node_s *> vertexQ;
  4. setVertexState(_vertex,VISITED);
  5. vertexQ.push(&vertexPtr[_vertex]);
  6. while (!vertexQ.empty())
  7. {
  8. Node_s * vertex_pop = vertexQ.front();
  9. vertexQ.pop();
  10. std::cout << vertex_pop->verIndex<<" ";
  11. Node_s * node = vertex_pop->NEXT;
  12. while (node)
  13. {
  14. if (getVertexState(node->verIndex) == NOVISIT)
  15. {
  16. setVertexState(node->verIndex,VISITED);
  17. vertexQ.push(&vertexPtr[node->verIndex]);
  18. }
  19. node = node->NEXT;
  20. }
  21. }
  22. std::cout <<std::endl;
  23. }

2.DFS

  DFS的实现需要至少需要两个函数,一个负责调用,一个负责递归.

  1. void CGraph::traversal_dfs(int _vertex){
  2. updateVisited();
  3. dfs(_vertex);
  4. std::cout <<std::endl;
  5. }

负责递归的dfs函数:

  1. void CGraph::dfs(int _vertex){
  2. std::cout << _vertex <<" ";
  3. setVertexState(_vertex,VISITED);
  4. for (Node_s * node = vertexPtr[_vertex].NEXT;node;node=node->NEXT)
  5. {
  6. if (getVertexState(node->verIndex) == NOVISIT)
  7. {
  8. dfs(node->verIndex);
  9. }
  10. }
  11. }

%DFS示意

3.在旅游时遇到的一个小问题

假期去张家界天门山旅游,走了一大圈发现还挺大;

就寻思着能不能找到一条路把全部景点都走完.这个图论中有讲过啊!!

问题抽象如下:





我把图中的点转化为图,并进行编号.

(左下角的那个点为0,由虚线连接的那个为9),共十个点

然后按照 顶点数 + "起始点-终点-权值" 的格式写入文件: 权值暂时没意义 (也不是,见后文)

10

0 1 50

1 0 50

1 2 50

...

8 9 50

9 8 50

9 7 50

其实最终要的线路要求如下:

  • 起点是0, 只能从0上山
  • 终点是9 , 因为只有9才能下山
  • 需要走过的顶点数尽可能多

利用回溯,DFS上场:

  1. // 一条线路的定义
  2. struct path_s{
  3. path_s():totalWeight(0),vertexVec(){}
  4. // 顶点集合
  5. std::vector<int> vertexVec;
  6. // 总的权值
  7. WEIGHTYPE totalWeight;
  8. };
  9. void CGraph::throughPathLongest(int _vertex){
  10. path_s path;
  11. dfs_path(path , _vertex);
  12. }
  13. void CGraph::dfs_path(path_s & _vertexPath,int _vertex){
  14. _vertexPath.vertexVec.push_back(_vertex);
  15. setVertexState(_vertex, VISITED);
  16. // 存储把当前路线
  17. addTolongestPath(_vertexPath);
  18. for (Node_s * head = vertexPtr[_vertex].NEXT;head;head=head->NEXT)
  19. {
  20. if (getVertexState(node->verIndex) == NOVISIT)
  21. {
  22. _vertexPath.totalWeight += head->weight;
  23. dfs_path(_vertexPath,head->data);
  24. // 回溯,处理当前节点
  25. _vertexPath.totalWeight -= head->weight;
  26. _vertexPath.vertexVec.pop_back();
  27. setVertexState(_vertex, NOVISIT);
  28. }
  29. }
  30. }
  31. // 根据加入线路的规则,具体实现如下
  32. int CGraph::addTolongestPath(path_s &_vertexPath){
  33. // 若路径为空,则直接设置为当前路径
  34. if (pathVec.empty())
  35. {
  36. pathVec.push_back(_vertexPath);
  37. }
  38. else
  39. {
  40. // 终点是9,而且要大于已经存储的线路所含节点数
  41. if (_vertexPath.vertexVec.back() == 9 &&
  42. _vertexPath.vertexVec.size() > pathVec.back().vertexVec.size())
  43. {
  44. pathVec.clear();
  45. pathVec.push_back( _vertexPath);
  46. }
  47. else
  48. // 否则只是一条含有相同目的地所经过顶点不同的线路而已.
  49. if (
  50. _vertexPath.vertexVec.back() == 9 &&
  51. _vertexPath.vertexVec.size() == pathVec.back().vertexVec.size())
  52. {
  53. pathVec.push_back(_vertexPath);
  54. }
  55. }
  56. }

最后显示一下pathVec中的结果即可:

结果显示如下:

NO.0:

0->1->2->3->4->5->6->7->9 (400)

NO.1:

0->1->2->3->4->5->6->8->9 (400)

哈哈,我们选择的是NO.1的线路,因为那边有风景看.(其实之前我们坐到7又返回了...

相关代码见我的github

里面有让你走完全部景点的路径,不过终点不是9 ,这意味着我逛完后还要重复的线路到9:(

突然觉得景点有点鸡贼 (逃

总结:

  • BFS就是一个函数,而且没有显示的使用堆栈,这对大数据的遍历很有利;
  • DFS对于寻找要求的路径很有好处,但是递归太深是个需要考虑的地方;
  • 出去旅游有必要先写一个程序判断能够看完所有景点的最佳路径:)

图之BFS和DFS遍历的实现并解决一次旅游中发现的问题的更多相关文章

  1. 【算法】二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现记录(Java版)

    本文总结了刷LeetCode过程中,有关树的遍历的相关代码实现,包括了二叉树.N叉树先序.中序.后序.BFS.DFS遍历的递归和迭代实现.这也是解决树的遍历问题的固定套路. 一.二叉树的先序.中序.后 ...

  2. 【数据结构与算法】自己动手实现图的BFS和DFS(附完整源码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/19617187 图的存储结构 本文的重点在于图的深度优先搜索(DFS)和广度优先搜索(BFS ...

  3. PAT Advanced 1034 Head of a Gang (30) [图的遍历,BFS,DFS,并查集]

    题目 One way that the police finds the head of a gang is to check people's phone calls. If there is a ...

  4. 广度优先遍历-BFS、深度优先遍历-DFS

    广度优先遍历-BFS 广度优先遍历类似与二叉树的层序遍历算法,它的基本思想是:首先访问起始顶点v,接着由v出发,依次访问v的各个未访问的顶点w1 w2 w3....wn,然后再依次访问w1 w2 w3 ...

  5. 二叉树:前序遍历、中序遍历、后序遍历,BFS,DFS

    1.定义 一棵二叉树由根结点.左子树和右子树三部分组成,若规定 D.L.R 分别代表遍历根结点.遍历左子树.遍历右子树,则二叉树的遍历方式有 6 种:DLR.DRL.LDR.LRD.RDL.RLD.由 ...

  6. 数据结构(12) -- 图的邻接矩阵的DFS和BFS

    //////////////////////////////////////////////////////// //图的邻接矩阵的DFS和BFS ////////////////////////// ...

  7. 邻接表存储图,DFS遍历图的java代码实现

    import java.util.*; public class Main{ static int MAX_VERTEXNUM = 100; static int [] visited = new i ...

  8. BFS、DFS、先序、中序、后序遍历的非递归算法(java)

    一 广度优先遍历(BFS) //广度优先遍历二叉树,借助队列,queue public static void bfs(TreeNode root){ Queue<TreeNode> qu ...

  9. 【数据结构】4.1图的创建及DFS深度遍历(不完善)

    声明:本代码仅供参考,根本就不是正确代码(至少在我看来,有很多BUG和不完美的地方) 图的存储方式选择为邻接表,并且headNode只是来存储一个链表的Node首地址额 总之这个代码写的很垃圾呀很垃圾 ...

随机推荐

  1. 【springmvc】之常用的注解

    原理这里不叙述,只讲怎么用 1. spring mvc中的@PathVariable是用来获得请求url中的动态参数的 @RequestMapping(value="/user/{userI ...

  2. SVN服务器几种备份策略---重点svnsync备份---OK

    配置管理的一个重要使命是保证数据的安全性,防止服务器应硬盘损坏.误操作造成数据无法恢复的灾难性后果.因此制定一个完整的备份策略非常重要. 一般来说,备份策略应规定如下几部分内容:备份频度.备份方式.备 ...

  3. Linux 2.6 内核实时性分析 (完善中...)

      经过一个月的学习,目前对linux 下驱动程序的编写有了入门的认识,现在需要着手实践,编写相关的驱动程序. 因为飞控系统对实时性有一定的要求,所以先打算学习linux 2.6 内核的实时性与任务调 ...

  4. 关于c语言中qsort函数的一点心得

    今天写c时无意间用到了排序,便想着使用c语言标准库中提供的排序函数,即qsort函数(c++stl中提供了sort函数用于排序),首先是介绍qsort函数的一些基本用法(以下内容转自: http:// ...

  5. python(21)实现多进程

    参考链接:http://www.cnblogs.com/kaituorensheng/p/4445418.html python多进程:multiprocessing python中的多线程其实并不是 ...

  6. c#4

    float translation = Time.deltaTime * 10; transform.Translate(0, 0, translation);//沿z轴移动   public cla ...

  7. JAVA break终止循环,与continue进入下一次循环

    一.break终止循环 在循环中,遇到break;将会跳出循环,继续往下执行代码 public class Test{ public static void main(String[] args){ ...

  8. tcpdump命令--实用篇

    //查看本机与mysql的操作命令 注意 -i any表示监听所有网络接口,我们也根据自身情况选择网络接口 #tcpdump -i any -w - dst port 3306 |strings // ...

  9. java多线程之从任务中获取返回值

    package wzh.test; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util ...

  10. 30天轻松学习javaweb_https协议的密码学

    https通过非对称加密实现数据安全1.CA机构提供数字证书,其中数字证书包含公钥.2.浏览器自带功能验证数字证书是否是CA机构颁发的.3.根据数字证书包含的公钥对表单数据进行加密.4.公钥提供方再根 ...