数据结构之图 篇二——普利姆算法与克鲁斯算法实现最小生成树

最小生成树

 

普里姆算法(Prim算法)

是图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。

https://baike.baidu.com/item/Prim/10242166?fromtitle=%E6%99%AE%E9%87%8C%E5%A7%86%E7%AE%97%E6%B3%95&fromid=4255724#3

下面是算法的代码实现 

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值