二维动态数组与图的遍历
2016-01-25 22:54
375 查看
1. 二维动态数组
一般图结构创建是根据邻接矩阵的定义,采用链表的的方式实现。对于这里的邻接矩阵借鉴了存储图像数据的动态二维数组结构,他的构造和析构如下:const int num = 5; //分配空间 int **array = new int* [num]; for (int i=0; i<num; i++) { <span style="white-space:pre"> </span>array[i] = new int[num]; }
//释放资源 for (int i=0; i<num; i++) { delete[] array[i]; } delete []array;
在C++11标准里面可以采用std::tr1::shared_ptr 来管理这片内存,特别是当涉及到跨文件进行处理的时候,因忘记析构而内存泄露的风险会减小很多。
2. 图及其遍历
2.1 基于链式存储的图数据结构
首先要一个数组,里面包含图的边数和顶点数。每个数组元素里面包含数据域和指向连接表头结点的头指针。//基于链式存储的邻接表图数据结构 const int MAXVEX = 20; //定义图的最大顶点数目 typedef char DataType; typedef struct arc //定义表结点类型 { int adjvex; //顶点序号 int weight; //权值 struct arc* next; //指向下一个邻接点的指针 }ArcNode, *PArcNode; typedef struct //顶点表结点类型 { DataType data; //顶点信息 ArcNode* head; //编表头指针 }VexNode; typedef struct //邻接表类型 { VexNode list[MAXVEX]; //顶点表 int edges, vexs; //用于统计图的顶点与边数 }VGraph, *AdjList;
2.2 图的创建
图的创建是通过二维for循环实现的,外层for循环实现头结点和顶结点数据域的初始化,内层for循环实现与每个相连的顶点的创建(链表实现)和设置权值。/************************************************************************/ /* 创建图结构 输入参数 grap: 邻接表类型指针 m: 邻接矩阵的数据 n: 顶点的总数 返回值 成功返回true 失败返回false */ /************************************************************************/ bool CGraphLink::CreateGraph(VGraph* grap, int** m, int n) { if ((nullptr==grap) || (nullptr==m) || (0>n)) { return false; } grap->edges = 0; //初始化边的总数目和顶点的数目为0 grap->vexs = n; for (int i=0; i<n; i++) { grap->list[i].data = 'A'+i; //给顶点信息赋值 grap->list[i].head = nullptr; //初始化头指针为空 for (int j=0; j<n; j++) { if (0 != m[i][j]) //如果二维数组m中的对应元素非0 { ArcNode *p = new ArcNode; if (nullptr == p) //分配内存错误 { std::cout << "\n分配内存错误!\n"; return false; } p->adjvex = j; //设置顶点序号 p->next = grap->list[i].head; //从链表头部插入新结点 p->weight = m[i][j]; //设置权值 grap->list[i].head = p; //设置头指针指向p grap->edges++; //边数加1 } } } return true; }
2.3 深度优先遍历
/************************************************************************/ /* 连通图的深度优先遍历方法 递归实现 grap: 输入的图结构 k: 起始的顶点序号 visited: 已经访问过的顶点的存储数组 */ /************************************************************************/ void CGraphLink::DFS(VGraph* grap, int k, int* visited) { std::cout << grap->list[k].data; //显示顶点数据 visited[k] = 1; //标记已经访问了的顶点 int u = GetFirst(grap, k); //获取头结点指向的下一个顶点的编号 while (-1 != u) //还有节点存在 { if (0 == visited[u]) //判断顶点是否被访问过 { DFS(grap, u, visited); //若没有被访问过递归调用进行访问 } u = GetNext(grap, k, u); //获取下一个邻接点 } }
/************************************************************************/ /* 获取图邻接表的第k+1个的第一个顶点编号 grap: 输入的图结构 k: 第K+1个邻接链表,顶点的序号 返回值: 滴K+1个邻接链表的第一个值 若为空则返回为-1 */ /************************************************************************/ int CGraphLink::GetFirst(VGraph* grap, int k) { if ((k<0) || (k>grap->vexs)) { std::cout << "\n输入的K超出了范围!\n"; return -1; } if (nullptr == grap->list[k].head) { std::cout << "\n需要获取的连接表头指针为空!\n"; return -1; } else { return grap->list[k].head->adjvex; } } /************************************************************************/ /* 在顶点k的边表中获取下一个邻接点 先在边表中找u,找到u之后,u的next指针域即指向下一个邻接点 */ /************************************************************************/ int CGraphLink::GetNext(VGraph* grap, int k, int u) { if ((k<0) || (k>grap->vexs) || (u<0) || (u>grap->vexs)) { std::cout << "\n输入的K超出了范围!\n"; return -1; } PArcNode p = grap->list[k].head; while ((nullptr!=p->next) && (p->adjvex!=u)) { p = p->next; } if (nullptr == p->next) { return -1; } else { return p->next->adjvex; } }
2.4 广度优先遍历
/************************************************************************/ /* 连通图数据结构的广度优先遍历 #_# 非连通图的遍历参照广度优先非连通图的遍历实现 #_# grap: 需要广度优先遍历的图结构 k: 广度优先遍历的其实顶点 返回值 连通图遍历成功返回true 失败返回false */ /************************************************************************/ bool CGraphLink::BFS(VGraph* grap, int k, const int num) { //前期错误检查 if ((nullptr==grap) || (0>k)) { std::cout << "\nSFS error occurred\n"; return false; } if (nullptr == grap->list[k].head) { std::cout << "\nBFS pos error\n"; return false; } int *visited = new int[num]; //构造一个访问与否标志位判别数组 0(没访问过)1(访问过) memset(visited, 0, sizeof(int)*num); //将数组清零 也是初始化 std::vector<VexNode> vec; //定义一个向量 存储遍历到的顶点标号 vec.push_back(grap->list[k]); //将初始的顶点 压入到向量中 类型为VexNode std::cout << "\n" << vec[k].data; //显示顶点处的信息 VecNode里面 visited[k] = 1; //设置初始位置的访问标志位为1 PArcNode p = nullptr; //定义一个遍历边链表的中间变量 /************************************************************************/ /* 遍历的思想(个人理解): 图结构的广度优先遍历与树结构的层次遍历具有相似的地方,下面的方法也是借鉴树结构的层次得来的。(回顾:在树的层次遍历中定义了cur和end两个 向量当前位置和长度标志变量,在第一层while中判别cur<vec.size();是遍历结束的条件也就是向量的元素个数不再增加。之后vec中cur到end元素的 左右孩子指针增加vec中元素的个数,直到最后遍历完成,vec中的元素不再增加cur=vec.size())。而在图结构中采用了回溯思想,每次取向量的最后 一个元素的头指针(访问过的顶点就删除出向量),以这个头指针为起点依次将没有走过的顶点存入向量中,也将数组中对应标志位置1。直到向量中的 元素为空,则图的广度优先遍历完成。 */ /************************************************************************/ while (!vec.empty()) //判断向量是否为空 若为非空则继续遍历 { VexNode q = vec.back(); //取出向量最后的顶点 p = q.head; //将它的头结点的指针取出 vec.pop_back(); //删除最后向量的最后一个元素 回溯思想 while (p) //判断p是不是空指针 { if (0 == visited[p->adjvex]) //判断p中存储的对应顶点编号有没有被访问过 { std::cout << "\n" << grap->list[p->adjvex].data; //显示对应顶点序号处 顶点的信息 data的值 visited[p->adjvex] = 1; //将访问过的顶点编号处的标志位赋值为1 (顶点标号与list里面的标号对应N*N的矩阵) vec.push_back(grap->list[p->adjvex]); //将对应序号的顶点(VexNode)存入向量 } p = p->next; //指向下一个顶点编号 } } return true; }
相关文章推荐
- leetcode:Coin Change
- JAVA 文本框、密码框、标签
- 设置文本框失去焦点与获得焦点的样式
- 阿里面试总结--JAVA
- poj1006 扩展欧几里得算法+中国剩余定理
- SQLite数据插入异常
- 备忘录
- SQLite数据插入异常
- 关于struts2的OGNL(二)
- JAVA 综合布局应用
- Android activity属性设置大全
- 2013年第四届蓝桥杯B组(C/C++)预赛题目及个人答案(欢迎指正)
- 阿岳之_DNS正反向解析库配置篇(一)
- spring--hibernate 事务
- JAVA 网格布局管理器
- 扩展欧几里得算法
- 在Xcode中使用Git进行源码版本控制
- 观察者模式
- serlvet的生命周期是怎样的...
- 自定义的servlet的 doGet,doPost是怎样被调用到的..