经过漫长的学习,终于对图的结构、表示和遍历及其最小生成树等一系列图相关的操作有了一些理解,因此现在写这篇博客,希望会对大家有用,我们的图是通过邻接矩阵实现的。
Node.h
#ifndef NODE_H
#define NODE_H
class Node{
public:
Node(char data = 0);
char m_data;
bool m_isVisited;
};
#endif
Edge.h
#ifndef EDGE_H
#define EDGE_H
class Edge{
public:
Edge(int nodeIndexA = 0, int nodexIndexB = 0, int weightValue = 0);
int m_nodeIndexA;
int m_nodeIndexB;
int m_weightValue;
bool m_selected;
};
#endif
CMap.h
#ifndef CMAP_H
#define CMAP_H
#include <vector>
#include "Node.h"
#include "Edge.h"
using namespace std;
class CMap{
public:
CMap(int capacity);
~CMap();
bool addNode(Node* node);
void resetNode();
bool setValueToMatrixForDirectedGraph(int row, int col, int value = 1);//为有向图设置邻接矩阵
bool setValueToMatrixForUndirectedGraph(int row, int col, int value = 1);//为无向图设置邻接矩阵
void printMatrix();//打印邻接矩阵
//遍历
void depthFirstTraverse(int nodeIndex);
void breadthFirstTraverse(int nodeIndex);
void depthFirstTraverse_self(int nodeIndex);
void primTree(int nodeIndex);//最小生成树
void kruskalTree();
private:
int m_capacity;//图中最多容纳的顶点数
int m_iNodeCount;//已经添加的顶点的数目
Node* m_pNodeArray;//存放顶点数组
int* m_pMatrix;//存放二维数组矩阵,用来表示顶点之间的边的关系
bool getValueFromMatrix(int row, int col, int& val);//从矩阵中获取权值
int getMinEdge(vector<Edge> edgeVec);
void mergeNodeSet(vector<int> &nodeSetA,vector<int> nodeSetB);
bool isInset(vector<int> nodeSet,int target);
//申明一个边的集合用来保存最小生成树的边
Edge* m_edge;
};
#endif
Node.cpp
#include <iostream>
#include "Node.h"
using namespace std;
Node::Node(char data){
m_data = data;
m_isVisited = false;
}
Edge.cpp
#include "Edge.h"
Edge::Edge(int nodeIndexA, int nodeIndexB, int weightValue){
m_nodeIndexA = nodeIndexA;
m_nodeIndexB = nodeIndexB;
m_weightValue = weightValue;
m_selected = false;
}
CMap.cpp
#include <iostream>
#include "CMap.h"
#include <queue>
using namespace std;
CMap::CMap(int capacity)
{
m_capacity = capacity;
m_iNodeCount = 0;
m_pNodeArray = new Node[m_capacity];
m_pMatrix = new int[m_capacity*m_capacity];
memset(m_pMatrix, 0, m_capacity*m_capacity*sizeof(int));
//生成最小生成树的边
m_edge = new Edge[m_capacity - 1];
}
CMap::~CMap()
{
delete[] m_pNodeArray;
delete[] m_pMatrix;
delete[] m_edge;
}
bool CMap::addNode(Node* node)
{
m_pNodeArray[m_iNodeCount].m_data = node->m_data;
m_iNodeCount++;
return true;
}
void CMap::resetNode()
{
for (int i = 0; i < m_iNodeCount; i++){
m_pNodeArray[i].m_isVisited = false;
}
}
bool CMap::setValueToMatrixForDirectedGraph(int row, int col, int value)
{
if (row < 0 && row >= m_capacity) return false;
if (col < 0 && col >= m_capacity) return false;
m_pMatrix[row*m_capacity + col] = value;
return true;
}
bool CMap::setValueToMatrixForUndirectedGraph(int row, int col, int value)
{
if (row < 0 && row >= m_capacity) return false;
if (col < 0 && col >= m_capacity) return false;
m_pMatrix[row*m_capacity + col] = value;
m_pMatrix[col*m_capacity + row] = value;
return true;
}
void CMap::printMatrix()
{
for (int i = 0; i < m_capacity; i++){
for (int j = 0; j < m_capacity; j++){
cout<<m_pMatrix[i*m_capacity + j]<<" ";
}
cout << endl;
}
}
//遍历
void CMap::depthFirstTraverse(int nodeIndex)
{
int value = 0;
cout << m_pNodeArray[nodeIndex].m_data << " ";
m_pNodeArray[nodeIndex].m_isVisited = true;
for (int i = 0; i < m_capacity; i++){
//判断从当前节点到其他顶点c是否存在边或者弧
getValueFromMatrix(nodeIndex, i, value);
if (value == 1){
if (m_pNodeArray[i].m_isVisited){
continue;
}
else{
depthFirstTraverse(i);
}
}
else{
continue;
}
}
}
void CMap::depthFirstTraverse_self(int nodeIndex){
int value = 0;
cout << m_pNodeArray[nodeIndex].m_data << " ";
m_pNodeArray[nodeIndex].m_isVisited = true;
for (int i = 0; i < m_capacity; i++){
getValueFromMatrix(nodeIndex, i, value);
if (value !=0){
if (m_pNodeArray[i].m_isVisited == false){
depthFirstTraverse_self(i);
}
}
}
}
void CMap::breadthFirstTraverse(int nodeIndex)
{
int value = 0;
int temp = NULL;
queue<int> queue;
Node firstNode = NULL;
queue.push(nodeIndex);
m_pNodeArray[nodeIndex].m_isVisited = true;
while (!queue.empty()){
temp = queue.front();
firstNode = m_pNodeArray[temp];
cout << firstNode.m_data << " ";
queue.pop();
for (int i = 0; i < m_capacity; i++){
getValueFromMatrix(temp, i, value);
if (value != 0){
if (m_pNodeArray[i].m_isVisited == false){
queue.push(i);
m_pNodeArray[i].m_isVisited = true;
}
}
}
}
}
bool CMap::getValueFromMatrix(int row, int col, int& val)
{
if (row < 0 || row >= m_capacity){
return false;
}
if (col < 0 || col >= m_capacity){
return false;
}
val = m_pMatrix[row*m_capacity + col];
}
int CMap::getMinEdge(vector<Edge> edgeVec){
int minWeight = 0;
int edgeIndex = 0;
int i = 0;
//获取第一个没有被访问的边
for (; i < (int)edgeVec.size(); i++){
if (edgeVec[i].m_selected != true){
minWeight = edgeVec[i].m_weightValue;
break;
}
}
if (minWeight == 0){
return -1;
}
//从剩余的边中继续找,看看有没有比第一条边的值更小的边
for (; i < (int)edgeVec.size(); i++){
if (edgeVec[i].m_selected != true){
if (edgeVec[i].m_weightValue < minWeight){
minWeight = edgeVec[i].m_weightValue;
edgeIndex = i;
}
}
}
return edgeIndex;
}
void CMap::primTree(int nodeIndex){
int value = 0;
int edgeCount = 0;
vector<int> nodeVec;//描述顶点的集合
vector<Edge> edgeVec;//描述边的集合
cout << m_pNodeArray[nodeIndex].m_data << endl;
nodeVec.push_back(nodeIndex);
m_pNodeArray[nodeIndex].m_isVisited = true;
while (edgeCount < m_capacity - 1){
int temp = nodeVec.back();
int i = 0;
for (; i < m_capacity; i++){
getValueFromMatrix(temp, i, value);
if (value != 0){
if (m_pNodeArray[i].m_isVisited != true){
Edge edge(temp, i, value);
edgeVec.push_back(edge);
}
}
}
int edgeIndex=getMinEdge(edgeVec);
edgeVec[edgeIndex].m_selected = true;
cout << edgeVec[edgeIndex].m_nodeIndexA << "------" << edgeVec[edgeIndex].m_nodeIndexB << " ";
//将最小边放入最小生成树的边集合
m_edge[edgeCount] = edgeVec[edgeIndex];
edgeCount++;
//找出最小边所连接的另一个点,并将点放入点集合
int nextNodeIndex = edgeVec[edgeIndex].m_nodeIndexB;
nodeVec.push_back(nextNodeIndex);
m_pNodeArray[nextNodeIndex].m_isVisited = true;
cout << m_pNodeArray[nextNodeIndex].m_data << endl;
}
}
void CMap::mergeNodeSet(vector<int> &nodeSetA, vector<int> nodeSetB){
for (int i = 0; i < nodeSetB.size(); i++){
nodeSetA.push_back(nodeSetB[i]);
}
}
bool CMap::isInset(vector<int> nodeSet, int target){
for (int i = 0; i < nodeSet.size(); i++){
if (nodeSet[i] == target){
return true;
}
}
return false;
}
void CMap::kruskalTree(){
int value = 0;
int edgeCount = 0;
vector<vector<int>> nodeSets;
//得到所有边
vector<Edge> edgeVec;
for (int i = 0; i < m_capacity; i++){
for (int j = i + 1; j < m_capacity; j++){
getValueFromMatrix(i, j, value);
if (value != 0){
Edge edge(i, j, value);
edgeVec.push_back(edge);
}
}
}
//从所有边中取出组成最小生成树的边
//1.找到算法结束条件
//2.找到集合中的最小边
//3.找出最小边相连的点
//4.找出点所在的点集合
//5.根据点所在集合的不同做出不同处理
while (edgeCount < m_capacity - 1){
int minEdgeIndex = getMinEdge(edgeVec);
edgeVec[minEdgeIndex].m_selected = true;
int nodeAIndex=0, nodeBIndex = 0;
bool nodeAIsInSet = false;
bool nodeBIsInset = false;
nodeAIndex = edgeVec[minEdgeIndex].m_nodeIndexA;
nodeBIndex = edgeVec[minEdgeIndex].m_nodeIndexB;
int nodeAInSetLabel = -1;
int nodeBInsetLabel = -1;
for (int i = 0; i < (int)nodeSets.size(); i++){
//不可能有一个点同时存在两个集合中
nodeAIsInSet = isInset(nodeSets[i], nodeAIndex);
if (nodeAIsInSet){
nodeAInSetLabel = i;
}
}
for (int i = 0; i < (int)nodeSets.size(); i++){
//不可能有一个点同时存在两个集合中
nodeBIsInset = isInset(nodeSets[i], nodeBIndex);
if (nodeBIsInset){
nodeBInsetLabel = i;
}
}
if (nodeAInSetLabel == -1 && nodeBInsetLabel == -1){
vector<int> vec;
vec.push_back(nodeAIndex);
vec.push_back(nodeBIndex);
nodeSets.push_back(vec);
}
//A不属于任何集合,B属于某个集合
else if (nodeAInSetLabel == -1 && nodeBInsetLabel != -1){
nodeSets[nodeBInsetLabel].push_back(nodeAIndex);
}
else if (nodeBInsetLabel == -1 && nodeAInSetLabel != -1){
nodeSets[nodeAInSetLabel].push_back(nodeBInsetLabel);
}
//连接到两个集合中的不同点,需要合并两个集合
else if (nodeAInSetLabel != -1 && nodeBInsetLabel != -1 && nodeAInSetLabel != nodeBInsetLabel){
mergeNodeSet(nodeSets[nodeAInSetLabel], nodeSets[nodeBInsetLabel]);
for (int j = nodeBInsetLabel; j < (int)nodeSets.size() - 1; j++){
nodeSets[j] = nodeSets[j + 1];
}
}
//表示形成回路
else if (nodeAInSetLabel != -1 && nodeBInsetLabel != -1 && nodeAInSetLabel == nodeBInsetLabel){
continue;
}
m_edge[edgeCount] = edgeVec[minEdgeIndex];
edgeCount++;
cout << edgeVec[minEdgeIndex].m_nodeIndexA << "----" << edgeVec[minEdgeIndex].m_nodeIndexB << " ";
cout << edgeVec[minEdgeIndex].m_weightValue << endl;
}
}
CMapDemo.cpp
#include <iostream>
#include "CMap.h"
using namespace std;
int main(){
CMap* pMap = new CMap(8);
Node* pNodeA = new Node('A');
Node* pNodeB = new Node('B');
Node* pNodeC = new Node('C');
Node* pNodeD = new Node('D');
Node* pNodeE = new Node('E');
Node* pNodeF = new Node('F');
Node* pNodeG = new Node('G');
Node* pNodeH = new Node('H');
pMap->addNode(pNodeA);
pMap->addNode(pNodeB);
pMap->addNode(pNodeC);
pMap->addNode(pNodeD);
pMap->addNode(pNodeE);
pMap->addNode(pNodeF);
pMap->addNode(pNodeG);
pMap->addNode(pNodeH);
pMap->setValueToMatrixForUndirectedGraph(0,1);
pMap->setValueToMatrixForUndirectedGraph(0, 3);
pMap->setValueToMatrixForUndirectedGraph(1, 2);
pMap->setValueToMatrixForUndirectedGraph(1, 5);
pMap->setValueToMatrixForUndirectedGraph(3, 6);
pMap->setValueToMatrixForUndirectedGraph(3, 7);
pMap->setValueToMatrixForUndirectedGraph(6, 7);
pMap->setValueToMatrixForUndirectedGraph(2, 4);
pMap->setValueToMatrixForUndirectedGraph(4, 5);
pMap->printMatrix();
cout << "图的深度优先遍历:" << endl;
pMap->depthFirstTraverse_self(0);
cout << "\n换一种方法试试看:" << endl;
pMap->resetNode();
pMap->depthFirstTraverse(0);
pMap->resetNode();
cout << "\n图的广度优先遍历:" << endl;
pMap->breadthFirstTraverse(0);
cout << endl;
return 0;
}
CMapDemo_MiniGenerTree.cpp
#include <iostream>
#include "CMap.h"
using namespace std;
int main(){
CMap* pMap = new CMap(6);
Node* pNodeA = new Node('A');
Node* pNodeB = new Node('B');
Node* pNodeC = new Node('C');
Node* pNodeD = new Node('D');
Node* pNodeE = new Node('E');
Node* pNodeF = new Node('F');
pMap->addNode(pNodeA);
pMap->addNode(pNodeB);
pMap->addNode(pNodeC);
pMap->addNode(pNodeD);
pMap->addNode(pNodeE);
pMap->addNode(pNodeF);
pMap->setValueToMatrixForUndirectedGraph(0, 1, 6);
pMap->setValueToMatrixForUndirectedGraph(0, 4, 5);
pMap->setValueToMatrixForUndirectedGraph(0, 5, 1);
pMap->setValueToMatrixForUndirectedGraph(1, 2, 3);
pMap->setValueToMatrixForUndirectedGraph(1, 5, 2);
pMap->setValueToMatrixForUndirectedGraph(2, 5, 8);
pMap->setValueToMatrixForUndirectedGraph(2, 3, 7);
pMap->setValueToMatrixForUndirectedGraph(3, 5, 4);
pMap->setValueToMatrixForUndirectedGraph(3, 4, 2);
pMap->setValueToMatrixForUndirectedGraph(4, 5, 9);
pMap->primTree(0);
cout<<endl;
pMap->kruskalTree();
cout << endl;
return 0;
}
PS:CMapDemo.cpp主要是针对图的遍历,广度深度优先进行的,CMapDemo_MiniGenerTree.cpp主要是针对图的最小生成树进行设计的,希望大家仔细品味。
CMap用到的图:
运行结果:
CMapDemo_MiniGenerTree.cpp用到的图:
运行结果: