图的两种最小生成树算法之C++封装

来源:互联网 发布:西安软件新城地址 编辑:程序博客网 时间:2024/05/19 17:04

最小生成树定义:

    给定一无向带权图,顶点数是n,要使图连通只需n-1条边,若这n-1条边的权值和最小,则称有这n个顶点和n-1条边构成了图的最小生成树(minimum-cost spanning tree)MST。


两种最小生成树算法:

一、prim算法思想:设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V,再从集合U-V(差集)中找到另一点b使得点b到集合V中任意一点的权值最小,此时将b点也加入集合V;以此类推,现在的集合V={a,b},再从集合U-V(差集)中找到另一点c使得点c到集合V中任意一点的权值最小,此时将c点加入集合V,直至所有顶点全部被加入V,此时就构建出了一颗MST。因为有N个顶点,所以该MST就有N-1条边,每一次向集合V中加入一个点,就意味着找到一条MST的边。


假设有一无向图:


1、以v1作为起始点,初始时,V={v1}

2、从 U-V={v2,v3,v4,v5,v6}中选出到V={v1}中任意点最小权值点为v3,则V={v1,v3},则:


3、从 U-V={v2,v4,v5,v6}中选出到V={v1,v3}中任意点最小权值点为v6,则V={v1,v3,v6},则:


从 U-V={v2,v4,v5}中选出到V={v1,v3,v6}中任意点最小权值点为v4,则V={v1,v3,v6,v4},


依此类推,在不能形成环路的条件下,依次选出:v2,v5,则最后的最小生成树为:



kruskal算法:

算法思想:则令最小生成树的初始状态为只有n个顶点而无边的非连通图,图中每个顶点各自成一个连通分量。从图中的所有边中选出一条权值最小的边,若该边依附的两个顶点落在T不同的连通分量中,则将该边作为最小生成树的一条边保存起来,并标记该边已经选择过,否则就摒弃该条边而选择下一条代价最小的边,依次类推,直到图中所有顶点都在同一连通分量上为止。




图中先将每个顶点看作独立的子图,然后查找最小权值边,这条边是有限制条件的,边得两个顶点必须不在同一个子图中,如上图,第一次找到最小权值边为(v1,v3),且满足限制条件,继续查找到边(v4,v6),(v2,v5),(v3,v6),当查找到最后一条边时,仅仅只有(v2,v3)满足限制条件,其他的如(v3,v4),(v1,v4)都在一个子图里面,不满足条件,至此已经找到最小生成树的所有边。


两种算法的C++实现:


.h文件


#pragma once#include <vector>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*/class CEdge//边的类{public:CEdge(int NodeIndexA = 0,int NodeIndexB = 0,int WightValue = 0);int m_nNodeIndexA; //边的起始点int m_nNodeIndexB;//边的终点int m_nWeightValue;//边的权值bool m_bSelected;//表明此边是否被选择过};class CNode//点的类{public:CNode(char cData = 0);public:char m_cData;bool m_bIsVisited;};class CZzcGrapha{public:CZzcGrapha(int nCapacity);~CZzcGrapha(void);bool AddNodeToGrapha(CNode* pNode); //向图中增加节点void ResetNodeVisitFlag();           //将所有节点的访问标识置为falsebool SetValueToMatrixForDirectedGraph(int row,int col,int value = 1);//向有向图矩阵中设置值bool SetValueToMatrixForUnDirectedGraph(int row,int col,int value = 1);//向无向图矩阵设置值bool GetValueFromMatrix(int row,int col,int& value);//从邻接矩阵中获取值void PrintMatrix();//打印出图的邻接矩阵void DepthFirstTraverse(int nodeindex);//深度优先遍历void WidthFirstTraverse(int nodeindex);//广度优先遍历void WidthTraverseIteration(vector<int> prevec);void PrimTree(int nodeindex);//prim算法 最小生成树void Kruskal();//Kruskal 最小生成树private:int GetMinValueEdge(vector<CEdge> edgeVec);bool IsInSet(vector<int> nodeVec,int nodeIndex);void mergeNodeSet(vector<int> &nodeSetA,vector<int> nodeSetB);private:int m_nCapacity;        //图的容量(可以容纳的节点数)int m_nCurNodeCount;    //图中当前的节点个数CNode* m_pNodeArray;    //用来存放定点数据int* m_pMatrix;         //用来存放邻接矩阵数据CEdge* m_pEdge;         //用来存储找到的最小生成树的边};



.Cpp文件:

#include "StdAfx.h"#include "ZzcGrapha.h"#include "Windows.h"#include <iostream>using namespace std;CEdge::CEdge(int NodeIndexA,int NodeIndexB,int WightValue){m_nNodeIndexA = NodeIndexA;m_nNodeIndexB = NodeIndexB;m_nWeightValue = WightValue;m_bSelected = false;}CNode::CNode(char cData){m_cData = cData;m_bIsVisited = false;}CZzcGrapha::CZzcGrapha(int nCapacity){m_nCapacity      = nCapacity;m_nCurNodeCount  = 0;m_pNodeArray     = new CNode[m_nCapacity];m_pMatrix        = new int[m_nCapacity * m_nCapacity];ZeroMemory(m_pMatrix,m_nCapacity * m_nCapacity * sizeof(int));m_pEdge = new CEdge[m_nCapacity - 1];}CZzcGrapha::~CZzcGrapha(void){if (m_pNodeArray){delete[]m_pNodeArray;m_pNodeArray = NULL;}if (m_pMatrix){delete[]m_pMatrix;m_pMatrix = NULL;}if (m_pEdge){delete[]m_pEdge;m_pEdge = NULL;}}bool CZzcGrapha::AddNodeToGrapha(CNode* pNode){if(pNode == NULL) return false;m_pNodeArray[m_nCurNodeCount].m_cData = pNode->m_cData;m_nCurNodeCount++;return true;}void CZzcGrapha::ResetNodeVisitFlag(){for(int i = 0;i < m_nCapacity;i++){m_pNodeArray[i].m_bIsVisited = false;}}bool CZzcGrapha::SetValueToMatrixForDirectedGraph(int row,int col,int value){if(row < 0||row >= m_nCapacity) return false;if(col < 0||col >= m_nCapacity) return false;m_pMatrix[m_nCapacity * row + col] = value;return true;}bool CZzcGrapha::SetValueToMatrixForUnDirectedGraph(int row,int col,int value){if(row < 0||row >= m_nCapacity) return false;if(col < 0||col >= m_nCapacity) return false;m_pMatrix[m_nCapacity * row + col] = value;m_pMatrix[m_nCapacity * col + row] = value;return true;}bool CZzcGrapha::GetValueFromMatrix(int row,int col,int& value){if(row < 0||row >= m_nCapacity) return false;if(col < 0||col >= m_nCapacity) return false;value = m_pMatrix[m_nCapacity * row + col];return true;}void CZzcGrapha::PrintMatrix(){for (int i = 0;i < m_nCapacity;i++){for (int k = 0;k < m_nCapacity;k++){cout<<m_pMatrix[m_nCapacity * i + k]<<" "; }cout<<endl;}}void CZzcGrapha::DepthFirstTraverse(int nodeindex){int value = 0;cout<<m_pNodeArray[nodeindex].m_cData<<" ";m_pNodeArray[nodeindex].m_bIsVisited = true;for (int i = 0;i < m_nCapacity;i++){GetValueFromMatrix(nodeindex,i,value);if (value == 1){if(m_pNodeArray[i].m_bIsVisited == true) continue;DepthFirstTraverse(i);} else{continue;}}}void CZzcGrapha::WidthFirstTraverse(int nodeindex){cout<<m_pNodeArray[nodeindex].m_cData<<" ";m_pNodeArray[nodeindex].m_bIsVisited = true;vector<int> curVec;curVec.push_back(nodeindex);WidthTraverseIteration(curVec);}void CZzcGrapha::WidthTraverseIteration(vector<int> prevec){int value = 0;vector<int> curVec;for(int i = 0;i < (int)prevec.size();i++){for (int j = 0;j < m_nCapacity;j++){GetValueFromMatrix(prevec[i],j,value);if (value != 0){if(m_pNodeArray[j].m_bIsVisited) continue;cout<<m_pNodeArray[j].m_cData<<" ";m_pNodeArray[j].m_bIsVisited = true;curVec.push_back(j);} else{continue;}}}if(curVec.size() == 0) return;WidthTraverseIteration(curVec);}//参数nodeindex表示第一个加入到点集合中的点void CZzcGrapha::PrimTree(int nodeindex)//prim算法 最小生成树{int value = 0;//存放所取得的边的权值int edgeCount = 0;//标识所找到的边的数目vector<int> nodeVec;//存放所找到的点的索引的集合vector<CEdge> edgeVec;//存放所找到的边的集合nodeVec.push_back(nodeindex);//将第一个点的索引加入到点集合m_pNodeArray[nodeindex].m_bIsVisited = true;//第一个点已经被访问过了cout<<m_pNodeArray[nodeindex].m_cData<<endl;while (edgeCount < m_nCapacity - 1){int temp = nodeVec.back();for (int i = 0;i < m_nCapacity;i++)//寻找与temp点相连接的点{GetValueFromMatrix(temp,i,value);if (value != 0)//权值不为0,则两点相连接{if (m_pNodeArray[i].m_bIsVisited)//此点已经被访问过了{continue;} else{CEdge edge(temp,i,value);//构造temp与i两点之间的边edgeVec.push_back(edge);//将此边加入到边的集合}}}//for循环过后会找到与temp点连接的所有的边,下面找到权值最小的边,返回此边在集合中的索引int mixEdgeIndex = GetMinValueEdge(edgeVec);edgeVec[mixEdgeIndex].m_bSelected = true;cout<<edgeVec[mixEdgeIndex].m_nNodeIndexA<<"---"<<edgeVec[mixEdgeIndex].m_nNodeIndexB<<"   ";cout<<edgeVec[mixEdgeIndex].m_nWeightValue<<endl;cout<<m_pNodeArray[edgeVec[mixEdgeIndex].m_nNodeIndexB].m_cData<<endl;m_pEdge[edgeCount] = edgeVec[mixEdgeIndex];//保存找到的最小边edgeCount++;int nextNodeIndex = edgeVec[mixEdgeIndex].m_nNodeIndexB;//下次要加入到点集合中的索引nodeVec.push_back(nextNodeIndex);m_pNodeArray[nextNodeIndex].m_bIsVisited = true;}}int CZzcGrapha::GetMinValueEdge(vector<CEdge> edgeVec)//找到最小权值边{int value = 0;int edgeIndex = 0;int i = 0;for (;i < (int)edgeVec.size();i++){if (!edgeVec[i].m_bSelected){value = edgeVec[i].m_nWeightValue;edgeIndex = i;break;}}if (value == 0){return -1;}for (;i < (int)edgeVec.size();i++){if(edgeVec[i].m_bSelected) continue;if (value > edgeVec[i].m_nWeightValue){value = edgeVec[i].m_nWeightValue;edgeIndex = i;}}return edgeIndex;}void CZzcGrapha::Kruskal(){int value = 0,edgeCount = 0;vector<vector<int>> nodeSets;//存放点集合的数组,相当于是数组的数组vector<CEdge> edgeVec;//存放边的数组//第一步:找出所有边,并放入到边的数组中for (int i = 0;i < m_nCapacity - 1;i++){for (int k = i + 1;k < m_nCapacity;k++){GetValueFromMatrix(i,k,value);cout<<value<<" ";if (value != 0)//i和k两个点之间存在边{CEdge edge(i,k,value);edgeVec.push_back(edge);}}cout<<endl;}//第二步,从所有边中取出最小生成树的边while (edgeCount < m_nCapacity - 1){//从边的集合中找出最小边int minEdgeIndex = GetMinValueEdge(edgeVec);edgeVec[minEdgeIndex].m_bSelected = true;//取出最小边的两个点int nodeAIndex = edgeVec[minEdgeIndex].m_nNodeIndexA;int nodeBIndex = edgeVec[minEdgeIndex].m_nNodeIndexB;bool bNodeAIsInSet = false;bool bNodeBIsInSet = false;int nNodeAInSetLab = -1;int nNodeBInSetLab = -1;//分别找出最小边两个点所在的集合for (int i = 0;i < (int)nodeSets.size();i++){bNodeAIsInSet = IsInSet(nodeSets[i],nodeAIndex);//判断nodeAIndex点在哪个点集合中if (bNodeAIsInSet){nNodeAInSetLab = i;//将集合索引保存起来}}for (int i = 0;i < (int)nodeSets.size();i++){bNodeBIsInSet = IsInSet(nodeSets[i],nodeBIndex);//判断nodeAIndex点在哪个点集合中if (bNodeBIsInSet){nNodeBInSetLab = i;//将集合索引保存起来}}//两点都不在已经存在的集合中,新建一个集合放入集合的数组中if(nNodeAInSetLab == -1&&nNodeBInSetLab == -1){vector<int> vec;vec.push_back(nodeAIndex);vec.push_back(nodeBIndex);nodeSets.push_back(vec);}//nodeAIndex不在已经存在的集合中,nodeBIndex在已经存在的集合中,//将nodeAIndex放入到nodeBIndex所在的集合中else if(nNodeAInSetLab == -1&&nNodeBInSetLab != -1){nodeSets[nNodeBInSetLab].push_back(nodeAIndex);}//nodeAIndex在已经存在的集合中,nodeBIndex不在已经存在的集合中,//将nodeBIndex放入到nodeAIndex所在的集合中else if(nNodeAInSetLab != -1&&nNodeBInSetLab == -1){nodeSets[nNodeAInSetLab].push_back(nodeBIndex);}//两点在不同的集合中,合并两个集合else if(nNodeAInSetLab != -1&&nNodeBInSetLab != -1&&nNodeAInSetLab != nNodeBInSetLab){//参数2合并到参数1的集合中mergeNodeSet(nodeSets[nNodeAInSetLab],nodeSets[nNodeBInSetLab]);//将参数2集合从nodeSets集合中去掉for (int i = nNodeBInSetLab;i < (int)nodeSets.size()-1;i++){nodeSets[i] = nodeSets[i+1];}}//当期的两个点在同一个集合中,这就会形成回路,多以当期边要摒弃掉else if(nNodeAInSetLab != -1&&nNodeBInSetLab != -1&&nNodeAInSetLab == nNodeBInSetLab){continue;}//到这里说明找出的边符合要求,将此边保存起来m_pEdge[edgeCount] = edgeVec[minEdgeIndex];edgeCount++;cout<<edgeVec[minEdgeIndex].m_nNodeIndexA<<"---"<<edgeVec[minEdgeIndex].m_nNodeIndexB<<"   ";cout<<edgeVec[minEdgeIndex].m_nWeightValue<<endl;}}bool CZzcGrapha::IsInSet(vector<int> nodeVec,int nodeIndex){for (int i = 0;i < (int)nodeVec.size();i++){if (nodeVec[i] == nodeIndex){return true;}}return false;}void CZzcGrapha::mergeNodeSet(vector<int> &nodeSetA,vector<int> nodeSetB){for (int i = 0;i < (int)nodeSetB.size();i++){nodeSetA.push_back(nodeSetB[i]);}}

测试:

// 图.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include "ZzcGrapha.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){CZzcGrapha* pGrapha = new CZzcGrapha(6);CNode* pNodeA = new CNode('A');CNode* pNodeB = new CNode('B');CNode* pNodeC = new CNode('C');CNode* pNodeD = new CNode('D');CNode* pNodeE = new CNode('E');CNode* pNodeF = new CNode('F');pGrapha->AddNodeToGrapha(pNodeA);pGrapha->AddNodeToGrapha(pNodeB);pGrapha->AddNodeToGrapha(pNodeC);pGrapha->AddNodeToGrapha(pNodeD);pGrapha->AddNodeToGrapha(pNodeE);pGrapha->AddNodeToGrapha(pNodeF);pGrapha->SetValueToMatrixForUnDirectedGraph(0,1,6);pGrapha->SetValueToMatrixForUnDirectedGraph(0,4,5);pGrapha->SetValueToMatrixForUnDirectedGraph(0,5,1);pGrapha->SetValueToMatrixForUnDirectedGraph(1,2,3);pGrapha->SetValueToMatrixForUnDirectedGraph(1,5,2);pGrapha->SetValueToMatrixForUnDirectedGraph(2,5,8);pGrapha->SetValueToMatrixForUnDirectedGraph(2,3,7);pGrapha->SetValueToMatrixForUnDirectedGraph(3,5,4);pGrapha->SetValueToMatrixForUnDirectedGraph(3,4,2);pGrapha->SetValueToMatrixForUnDirectedGraph(4,5,9);pGrapha->PrintMatrix();cout<<endl;//pGrapha->DepthFirstTraverse(0);//cout<<endl;//pGrapha->ResetNodeVisitFlag();//pGrapha->WidthFirstTraverse(0);//pGrapha->PrimTree(0);pGrapha->Kruskal();return 0;}


原创粉丝点击