【算法和数据结构】1.8--数据结构之图(C++实现)
来源:互联网 发布:nginx php 张宴 编辑:程序博客网 时间:2024/05/01 18:31
图,一种复杂的数据结构,在实际生活中起着举足轻重的作用。如路网(线路规划),社交网络描述等等。现在我们就以下面这个简单无向图(图1)为例,来说明使用邻接矩阵 实现图在计算机中的存储和操作的方法。
图1
不同于树,我们在习惯上称呼如图中数字编号的店为顶点而非节点。在树的概念中,存在一个特殊的节点称之为根,其起着好似渔网的主绳的作用,缺之不可。而在图这种数据结构中,更加倾向于平等地看待每一个顶点。
在下面给出的实现中,我们用一维数组来表示邻接矩阵。若图有n个顶点,则数组大小即为n*n。第row行,col列的元素实际上存储在数组下标为row*n+col的位置。
关于邻接矩阵的定义这里不再赘述,下图2给出图1所示简单无向图的邻接矩阵,希望能够帮助读者理解。
图2
下面给出以邻接矩阵表示图的C++实现,其中封装了对图的常用操作(具体见代码注释):
/*节点类*/class Node{public: Node(char identifier = 0); char m_identifier; //顶点编号 bool m_isVisited; //顶点访问标志位:true表示已经被访问};Node::Node(char identifier){ m_identifier = identifier; m_isVisited = false;}/*图类*/class Graph{public: Graph(int capacity); ~Graph(); void resetNode(); //重置所有顶点的访问标志位为false,未访问 bool addNode(Node *pNode); //添加新顶点 bool addEdgeForUndirectedGraph(int row, int col, int val = 1); //添加边以构造无向图,val表示权值,默认连通 bool addEdgeForDirectedGraph(int row, int col, int val = 1); //添加边以构造有向图,val表示权值,默认连通 void printMatrix(); //打印邻接矩阵 void depthFirstTraverse(int nodeIndex); //深度优先遍历,指定第一个点 void widthFirstTraverse(int nodeIndex); //广度优先遍历,指定第一个点private: bool getValueOfEdge(int row, int col, int &val); //获取边权值 void widthFirstTraverseImplement(vector<int> preVec); //利用vector实现广度优先遍历 int m_iCapacity; //图容量,即申请的数组空间最多可容纳的顶点个数 int m_iNodeCount; //图的现有顶点个数 Node *m_pNodeArray; //存放顶点的数组 int *m_pMatrix; //为了方便,用一维数组存放邻接矩阵};Graph::Graph(int capacity){ m_iCapacity = capacity; m_iNodeCount = 0; m_pNodeArray = new Node[m_iCapacity]; m_pMatrix = new int[m_iCapacity*m_iCapacity]; for (int i = 0;i < m_iCapacity*m_iCapacity;i++) //初始化邻接矩阵 { m_pMatrix[i] = 0; }}Graph::~Graph(){ delete []m_pNodeArray; delete []m_pMatrix;}void Graph::resetNode(){ for (int i = 0;i < m_iNodeCount;i++) { m_pNodeArray[i].m_isVisited = false; }}bool Graph::addNode(Node *pNode){ if (pNode == NULL) return false; m_pNodeArray[m_iNodeCount].m_identifier = pNode->m_identifier; m_iNodeCount++; return true;}bool Graph::addEdgeForUndirectedGraph(int row, int col, int val){ if (row < 0 || row >= m_iCapacity) return false; if (col < 0 || col >= m_iCapacity) return false; m_pMatrix[row*m_iCapacity + col] = val; m_pMatrix[col*m_iCapacity + row] = val; return true;}bool Graph::addEdgeForDirectedGraph(int row, int col, int val){ if (row < 0 || row >= m_iCapacity) return false; if (col < 0 || col >= m_iCapacity) return false; m_pMatrix[row*m_iCapacity + col] = val; return true;}void Graph::printMatrix(){ for (int i = 0;i < m_iCapacity;i++) { for (int k = 0;k < m_iCapacity;k++) cout << m_pMatrix[i*m_iCapacity + k] << " "; cout << endl; }}void Graph::depthFirstTraverse(int nodeIndex){ int value = 0; //访问第一个顶点 cout << m_pNodeArray[nodeIndex].m_identifier << " "; m_pNodeArray[nodeIndex].m_isVisited = true; //访问其他顶点 for (int i = 0;i < m_iCapacity;i++) { getValueOfEdge(nodeIndex, i, value); if (value != 0) //当前顶点与指定顶点连通 { if (m_pNodeArray[i].m_isVisited == true) //当前顶点已被访问 continue; else //当前顶点没有被访问,则递归 { depthFirstTraverse(i); } } else //没有与指定顶点连通 { continue; } }}void Graph::widthFirstTraverse(int nodeIndex){ //访问第一个顶点 cout << m_pNodeArray[nodeIndex].m_identifier << " "; m_pNodeArray[nodeIndex].m_isVisited = true; vector<int> curVec; curVec.push_back(nodeIndex); //将第一个顶点存入一个数组 widthFirstTraverseImplement(curVec);}void Graph::widthFirstTraverseImplement(vector<int> preVec){ int value = 0; vector<int> curVec; //定义数组保存当前层的顶点 for (int j = 0;j < (int)preVec.size();j++) //依次访问传入数组中的每个顶点 { for (int i = 0;i < m_iCapacity;i++) //传入的数组中的顶点是否与其他顶点连接 { getValueOfEdge(preVec[j], i, value); if (value != 0) //连通 { if (m_pNodeArray[i].m_isVisited==true) //已经被访问 { continue; } else //没有被访问则访问 { cout << m_pNodeArray[i].m_identifier << " "; m_pNodeArray[i].m_isVisited = true; //保存当前点到数组 curVec.push_back(i); } } } } if (curVec.size()==0) //本层次无被访问的点,则终止 { return; } else { widthFirstTraverseImplement(curVec); }}bool Graph::getValueOfEdge(int row, int col, int &val){ if (row < 0 || row >= m_iCapacity) return false; if (col < 0 || col >= m_iCapacity) return false; val = m_pMatrix[row*m_iCapacity + col]; return true;}
特别需要提醒的是:
- 在实现图遍历时应用了C++标准数组模板vector,读者在使用时应该包含相关头文件。
- 构造边的时候封装了两个函数,一个是对于有向图,一个是对于无向图,读者对比两种图形的邻接矩阵和实现可以很容易理解。
另外,在前面对于二叉树的介绍中,我们使用了数组和链表两种方式来表示树。其实对于图来说,也拥有着两种表示方式。上述邻接矩阵的表示方法即使用数组,另外还有使用链表的如邻接表、十字链表、邻接多重表等等。他们在表示有向图、无向图和运算代价方面各有优劣,这里不再做详细介绍。有兴趣的读者朋友可以查阅相关资料。
0 0
- 【算法和数据结构】1.8--数据结构之图(C++实现)
- 数据结构(C实现)------- 最小生成树之Prim算法
- 数据结构(C实现)------- 最小生成树之Kruskal算法
- 数据结构之树和二叉树算法实现(C语言)
- 【算法和数据结构】1.9--图的最小生成树之Prim算法(C++实现)
- 【算法和数据结构】1.10--图的最小生成树之Kruskal算法(C++实现)
- 【算法和数据结构】1.6--数据结构之大顶堆(C++实现,并封装堆排序)
- 【算法和数据结构】1.7--数据结构之二叉树(C++实现)
- 数据结构之排序算法实现(选自大话数据结构)
- 数据结构:BF算法,KMP算法之C、C++的实现
- 数据结构复习之【数据结构和算法概念】
- 数据结构之【数据结构和算法概念】--复习
- 【算法和数据结构】1.4--数据结构之队列
- 【算法和数据结构】1.5--数据结构之栈
- 数据结构和算法(C语言)
- c/c++算法和数据结构
- 数据结构之单链表实现栈(C++)
- 数据结构之单链表实现(C++)
- gdb调试器常用指令
- Eclipse修改工程后项目无法启动
- JVM源码分析之Java类的加载过程
- SPF算法计算过程--例
- 基于flex弹性布局头尾固定,中间滚动效果实现
- 【算法和数据结构】1.8--数据结构之图(C++实现)
- 【Codeforces Gym】 100162G Lyndon Words
- Word中公式变量解释时破折号对齐方法
- Java8系列之重新认识HashMap
- 2005-《Anytime Dynamic A*: An Anytime, Replanning Algorithm》
- PKU 3468
- SpringBoot快速入门(一)
- Java网络爬虫--HTML DOM(HTML 基础)
- STM32L4超低功耗特性概述