最小生成树
普里姆算法(Prim算法)
是图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。
下面是算法的代码实现
void CMap::primTree(int nodeIndex)
{
int value = 0;
int edgeCount = 0;
vector<int> nodeVec;
vector<Edge> edgeVec;
cout << m_pNodeArray[nodeIndex].m_cData << endl;
nodeVec.push_back(nodeIndex);
m_pNodeArray[nodeIndex].m_bIsVisited = true;
while (edgeCount < m_iCapacity-1)
{
int temp = nodeVec.back();
for (int i = 0; i < m_iCapacity; i++)
{
getValueFromMatrix(temp, i, value);
if(value != 0)
{
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
Edge edge(temp, i, value);
edgeVec.push_back(edge);
}
}
}
//从可选边集合中找到最小权值的边
int edgeIndex = getMinEdge(edgeVec);
edgeVec[edgeIndex].m_bSelected = true;
cout << edgeVec[edgeIndex].m_iNodeIndexA << "----" << edgeVec[edgeIndex].m_iNodeIndexB << " " ;
cout << edgeVec[edgeIndex].m_iWeightValue << endl;
m_pEdge[edgeCount] = edgeVec[edgeIndex];
edgeCount++;
int nextNodeIndex = edgeVec[edgeIndex].m_iNodeIndexB;
nodeVec.push_back(nextNodeIndex);
m_pNodeArray[nextNodeIndex].m_bIsVisited = true;
cout << m_pNodeArray[nextNodeIndex].m_cData << endl;
}
}
Kruskal算法
是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪心算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。
https://baike.baidu.com/item/%E5%85%8B%E9%B2%81%E6%96%AF%E5%8D%A1%E5%B0%94%E7%AE%97%E6%B3%95/4455899
下面是算法的代码实现:
void CMap::kruskalTree()
{
int value = 0;
int edgeCount = 0;
vector< vector<int> > nodeSets;
//第一步:取出所有边
vector<Edge> edgeVec;
for (int i = 0; i < m_iCapacity; i++)
{
for (int k = i + 1; k < m_iCapacity; k++)
{
getValueFromMatrix(i, k, value);
if (value != 0)
{
Edge edge(i, k, value);
edgeVec.push_back(edge);
}
}
}
//第二步:从所有边中取出组成最小生成树的边
//1、找到算法结束条件
while (edgeCount < m_iCapacity - 1)
{
//2、从边集合中找到最小边
int minEdgeIndex = getMinEdge(edgeVec);
edgeVec[minEdgeIndex].m_bSelected = true;
//3、找到最小边连接的点
int nodeAIndex = edgeVec[minEdgeIndex].m_iNodeIndexA;
int nodeBIndex = edgeVec[minEdgeIndex].m_iNodeIndexB;
bool nodeAIsInSet = false;
bool nodeBIsInSet = false;
int nodeAInSetLabel = -1;
int nodeBInSetLabel = -1;
//4、找出点所在的点集合
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;
}
}
//5、根据点所在集合的不同做出不同处理
if (nodeAInSetLabel == -1 && nodeBInSetLabel == -1)
{
vector<int> vec;
vec.push_back(nodeAIndex);
vec.push_back(nodeBIndex);
nodeSets.push_back(vec);
}
else if (nodeAInSetLabel == -1 && nodeBInSetLabel != -1)
{
nodeSets[nodeBInSetLabel].push_back(nodeAIndex);
}
else if (nodeAInSetLabel != -1 && nodeBInSetLabel == -1)
{
nodeSets[nodeAInSetLabel].push_back(nodeBIndex);
}
else if(nodeAInSetLabel != -1 && nodeBInSetLabel != -1 && nodeAInSetLabel != nodeBInSetLabel)
{
mergeNodeSet(nodeSets[nodeAInSetLabel], nodeSets[nodeBInSetLabel]);
for (int k = nodeBInSetLabel; k < (int)nodeSets.size(); k++)
{
nodeSets[k] = nodeSets[k+1];
}
}
else if(nodeAInSetLabel != -1 && nodeBInSetLabel != -1 && nodeAInSetLabel == nodeBInSetLabel)
{
continue;
}
m_pEdge[edgeCount] = edgeVec[minEdgeIndex];
edgeCount++;
cout << edgeVec[minEdgeIndex].m_iNodeIndexA << "--" << edgeVec[minEdgeIndex].m_iNodeIndexB << " ";
cout << edgeVec[minEdgeIndex].m_iWeightValue << endl;
}
}
下面是一个上面两种算法的应用,代码比较多,预警提示!!!
1、Edge.h
#ifndef EDGE_H
#define EDGE_H
class Edge
{
public:
Edge(int nodeIndexA=0, int nodeIndexB=0, int weightValue=0);
int m_iNodeIndexA;
int m_iNodeIndexB;
int m_iWeightValue;
bool m_bSelected;
};
#endif
2、Edge.cpp
#include "Edge.h"
Edge::Edge(int nodeIndexA, int nodeIndexB, int weightValue)
{
m_iNodeIndexA = nodeIndexA;
m_iNodeIndexB = nodeIndexB;
m_iWeightValue = weightValue;
m_bSelected = false;
}
3、Node.h
#ifndef NODE_H
#define NODE_H
class Node
{
public:
Node(char data = 0);
char m_cData;
bool m_bIsVisited;
};
#endif
4、Node.cpp
#include "Node.h"
Node::Node(char data)
{
m_cData = data;
m_bIsVisited = false;
}
5、CMap.h
#ifndef CMAP_H
#define CMAP_H
#include <vector>
#include "Node.h"
#include "Edge.h"
#include <iostream>
using namespace std;
class CMap
{
public:
CMap(int capacity);
~CMap();
bool addNode(Node *pNode); //图中添加顶点
void resetNode(); //重置顶点
bool setValueToMatrixForDirectedGraph(int row, int col, int val = 1); //有向图设置邻接矩阵
bool setValueToMatrixForUndirectedGraph(int row, int col, int val = 1); //无向图设置邻接矩阵
void printMatrix(); //打印邻接矩阵
void depthFirstTraverse(int nodeIndex); //深度优先遍历
void breadthFirstTraverse(int nodeIndex); //广度优先遍历
void primTree(int nodeIndex); //普利姆算法生成树
void kruskalTree(); //克鲁斯卡尔算法生成树
private:
bool getValueFromMatrix(int row, int col, int &val); //从矩阵种获取权值
void breadthFirstTraverseImpl(vector<int> preVec); //广度优先遍历实现函数
int getMinEdge(vector<Edge> edgeVec); //获取最小边
bool isInSet(vector<int> nodeSet, int target); //判断顶点是否在集合中
void mergeNodeSet(vector<int> &nodeSetA, vector<int> nodeSetB); //合并两个顶点集合
private:
int m_iCapacity; //图片可容纳的顶点数
int m_iNodeCount; //已添加的顶点数
Node *m_pNodeArray; //存放顶点数组
int *m_pMatrix; //存放邻接矩阵
Edge *m_pEdge;
};
#endif
6、CMap.cpp
#include "CMap.h"
#include <iostream>
using namespace std;
#include <vector>
#include <cstring>
CMap::CMap(int capacity)
{
m_iCapacity = capacity;
m_iNodeCount = 0;
m_pNodeArray = new Node[m_iCapacity];
m_pMatrix = new int[m_iCapacity * m_iCapacity];
memset(m_pMatrix, 0, m_iCapacity * m_iCapacity * sizeof(int));
// for (int i = 0; i < m_iCapacity * m_iCapacity; i++)
// {
// m_pMatrix[i] = 0;
// }
m_pEdge = new Edge[m_iCapacity -1];
}
CMap:: ~CMap()
{
delete []m_pNodeArray;
delete []m_pMatrix;
delete []m_pEdge;
}
bool CMap::addNode(Node *pNode)
{
if (pNode == NULL)
{
return false;
}
m_pNodeArray[m_iNodeCount].m_cData = pNode->m_cData;
m_iNodeCount++;
return true;
}
void CMap::resetNode()
{
for (int i = 0; i < m_iNodeCount; i++)
{
m_pNodeArray[i].m_bIsVisited = false;
}
}
bool CMap::setValueToMatrixForDirectedGraph(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;
}
bool CMap::setValueToMatrixForUndirectedGraph(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 CMap::getValueFromMatrix(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;
}
void CMap::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 CMap::depthFirstTraverse(int nodeIndex)
{
int value = 0;
cout << m_pNodeArray[nodeIndex].m_cData << " ";
m_pNodeArray[nodeIndex].m_bIsVisited = true;
//通过邻接矩阵判断是否与其他的顶点有连接
for (int i = 0; i < m_iCapacity; i++)
{
getValueFromMatrix(nodeIndex, i, value);
if (value == 1) //判断有弧连接其他顶点
{
//再判断该店是否被访问过
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
depthFirstTraverse(i);
}
}
else
{
continue;
}
}
}
//广度优先遍历
void CMap::breadthFirstTraverse(int nodeIndex)
{
cout << m_pNodeArray[nodeIndex].m_cData << " ";
m_pNodeArray[nodeIndex].m_bIsVisited = true;
vector<int> curVec;
curVec.push_back(nodeIndex);
breadthFirstTraverseImpl(curVec);
}
void CMap::breadthFirstTraverseImpl(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++)
{
getValueFromMatrix(preVec[j], i, value);
if(value != 0)
{
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
cout << m_pNodeArray[i].m_cData << " ";
m_pNodeArray[i].m_bIsVisited = true;
curVec.push_back(i);
}
}
}
}
if (curVec.size() == 0)
{
return;
}
else
{
breadthFirstTraverseImpl(curVec);
}
}
void CMap::primTree(int nodeIndex)
{
int value = 0;
int edgeCount = 0;
vector<int> nodeVec;
vector<Edge> edgeVec;
cout << m_pNodeArray[nodeIndex].m_cData << endl;
nodeVec.push_back(nodeIndex);
m_pNodeArray[nodeIndex].m_bIsVisited = true;
while (edgeCount < m_iCapacity-1)
{
int temp = nodeVec.back();
for (int i = 0; i < m_iCapacity; i++)
{
getValueFromMatrix(temp, i, value);
if(value != 0)
{
if (m_pNodeArray[i].m_bIsVisited)
{
continue;
}
else
{
Edge edge(temp, i, value);
edgeVec.push_back(edge);
}
}
}
//从可选边集合中找到最小权值的边
int edgeIndex = getMinEdge(edgeVec);
edgeVec[edgeIndex].m_bSelected = true;
cout << edgeVec[edgeIndex].m_iNodeIndexA << "----" << edgeVec[edgeIndex].m_iNodeIndexB << " " ;
cout << edgeVec[edgeIndex].m_iWeightValue << endl;
m_pEdge[edgeCount] = edgeVec[edgeIndex];
edgeCount++;
int nextNodeIndex = edgeVec[edgeIndex].m_iNodeIndexB;
nodeVec.push_back(nextNodeIndex);
m_pNodeArray[nextNodeIndex].m_bIsVisited = true;
cout << m_pNodeArray[nextNodeIndex].m_cData << endl;
}
}
int CMap::getMinEdge(vector<Edge> edgeVec)
{
int minWeight = 0;
int edgeIndex = 0;
int i = 0;
for(i = 0; i < (int)edgeVec.size(); i++)
{
if (!edgeVec[i].m_bSelected)
{
minWeight = edgeVec[i].m_iWeightValue;
edgeIndex = i;
break;
}
}
if (minWeight == 0)
{
return -1;
}
for ( ; i< (int)edgeVec.size(); i++)
{
if (edgeVec[i].m_bSelected)
{
continue;
}
else
{
if (minWeight > edgeVec[i].m_iWeightValue)
{
minWeight = edgeVec[i].m_iWeightValue;
edgeIndex = i;
}
}
}
return edgeIndex;
}
void CMap::kruskalTree()
{
int value = 0;
int edgeCount = 0;
vector< vector<int> > nodeSets;
//第一步:取出所有边
vector<Edge> edgeVec;
for (int i = 0; i < m_iCapacity; i++)
{
for (int k = i + 1; k < m_iCapacity; k++)
{
getValueFromMatrix(i, k, value);
if (value != 0)
{
Edge edge(i, k, value);
edgeVec.push_back(edge);
}
}
}
//第二步:从所有边中取出组成最小生成树的边
//1、找到算法结束条件
while (edgeCount < m_iCapacity - 1)
{
//2、从边集合中找到最小边
int minEdgeIndex = getMinEdge(edgeVec);
edgeVec[minEdgeIndex].m_bSelected = true;
//3、找到最小边连接的点
int nodeAIndex = edgeVec[minEdgeIndex].m_iNodeIndexA;
int nodeBIndex = edgeVec[minEdgeIndex].m_iNodeIndexB;
bool nodeAIsInSet = false;
bool nodeBIsInSet = false;
int nodeAInSetLabel = -1;
int nodeBInSetLabel = -1;
//4、找出点所在的点集合
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;
}
}
//5、根据点所在集合的不同做出不同处理
if (nodeAInSetLabel == -1 && nodeBInSetLabel == -1)
{
vector<int> vec;
vec.push_back(nodeAIndex);
vec.push_back(nodeBIndex);
nodeSets.push_back(vec);
}
else if (nodeAInSetLabel == -1 && nodeBInSetLabel != -1)
{
nodeSets[nodeBInSetLabel].push_back(nodeAIndex);
}
else if (nodeAInSetLabel != -1 && nodeBInSetLabel == -1)
{
nodeSets[nodeAInSetLabel].push_back(nodeBIndex);
}
else if(nodeAInSetLabel != -1 && nodeBInSetLabel != -1 && nodeAInSetLabel != nodeBInSetLabel)
{
mergeNodeSet(nodeSets[nodeAInSetLabel], nodeSets[nodeBInSetLabel]);
for (int k = nodeBInSetLabel; k < (int)nodeSets.size(); k++)
{
nodeSets[k] = nodeSets[k+1];
}
}
else if(nodeAInSetLabel != -1 && nodeBInSetLabel != -1 && nodeAInSetLabel == nodeBInSetLabel)
{
continue;
}
m_pEdge[edgeCount] = edgeVec[minEdgeIndex];
edgeCount++;
cout << edgeVec[minEdgeIndex].m_iNodeIndexA << "--" << edgeVec[minEdgeIndex].m_iNodeIndexB << " ";
cout << edgeVec[minEdgeIndex].m_iWeightValue << endl;
}
}
bool CMap::isInSet(vector<int> nodeSet, int target)
{
for (int i = 0; i < (int)nodeSet.size(); i++)
{
if (nodeSet[i] == target)
{
return true;
}
}
return false;
}
void CMap::mergeNodeSet(vector<int> &nodeSetA, vector<int> nodeSetB)
{
for (int i = 0; i < (int)nodeSetB.size(); i++)
{
nodeSetA.push_back(nodeSetB[i]);
}
}
7、main.cpp
#include <iostream>
#include "Edge.h"
#include "Node.h"
#include "CMap.h"
using namespace std;
/*
A
/ | \
B---F---E
\ /\ /
C----D
A B C D E F
0 1 2 3 4 5
A-B 6, A-E 5, A-F 1
B-C 3, B-F 2
C-F 8, C-D 7
D-F 4, D-E 2
E-F 9
*/
int main(int argc, char** argv)
{
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, 3);
pMap->setValueToMatrixForUndirectedGraph(3, 4, 2);
pMap->setValueToMatrixForUndirectedGraph(4, 5, 9);
cout << "普利姆算法:" << endl;
pMap->primTree(0);
cout << endl;
cout << "克鲁斯算法:" << endl;
pMap->kruskalTree();
cout << endl;
return 0;
}