// 无向图的最小支撑树Kruskal算法的实现
//主题: 用邻接表的方式实现最小支撑树Kruskal算法
//作者:Andyhou
//时间:2008年4月28日
//具体算法实现:
// 采用的是最小堆的方式找最小边,用等价类的方式步步合并最小的边。
// 程序中还声明了一个等价类。
#include <iostream>
using namespace std;
const int MaxNode=100;
const int VISITED=1;
const int UNVISITED=0;
const int ROOT=-1;
const int INFINITY=9999;
struct Node
{
int adjvex; //终点顶点
int weight; //权
struct Node *next; //指向下个顶点的指针
};
class VNode
{
public:
int data;
struct Node* first; //指向第一个邻接顶点
VNode(){}
VNode(int Data,struct Node* firstVal)
{
data=Data;
first=firstVal;
}
VNode(struct Node* firstVal)
{
first=firstVal;
}
};
//用于最小支撑树的边类
class Edge
{
public:
int from,to,weight;
Edge()
{
from=-1;
to=-1;
weight=0;
}
Edge(int f,int t,int w)
{
from=f;
to=t;
weight=w;
}
bool operator>(Edge oneEdge)
{
return weight>oneEdge.weight;
}
bool operator<(Edge oneEdge)
{
return weight<oneEdge.weight;
}
bool operator==(Edge oneEdge)
{
return weight==oneEdge.weight;
}
};
//用于”父指针"发表示等价类的类
class Gentree
{
private:
int *Array;
int size;
public:
Gentree(int sz)
{
size=sz;
Array=new int[sz+1];
for(int i=1;i<=sz;i++)
Array[i]=ROOT; //整个数的根节点
}
~Gentree()
{
delete[] Array;
}
int FIND(int curr) //寻找根的函数
{
while(Array[curr]!=ROOT)
curr=Array[curr];
return curr;
}
void UNION(int a,int b) //将a和b合并到一个等价类中,a和b在一个等价类中就他们相同的根。
{
int root1=FIND(a);
int root2=FIND(b);
if(root1!=root2)
Array[root2]=root1;
}
bool differ(int a,int b) //判断a和b是否等价。
{
int root1=FIND(a);
int root2=FIND(b);
return root1!=root2;
}
};
//最小值堆的建立
template <class T>
class MinHeap
{
private:
T* heapArray;
int CurrentSize;
int MaxSize;
public:
MinHeap(const int n);
virtual ~MinHeap()
{delete []heapArray;};
void BuildHeap();
bool isLeaf(int pos) const;
int leftchild(int pos) const;
int rightchild(int pos) const;
// Return parent position
int parent(int pos) const;
// 删除给定下标的元素
bool Remove(int pos, T& node);
void SiftDown(int left);
//从position向上开始调整,使序列成为堆
void SiftUp(int position);
bool Insert(const T& newNode);
T& RemoveMin();
bool empty()
{
if(CurrentSize>0)
return false;
else
return true;
}
};
template<class T>
MinHeap<T>::MinHeap(const int n)
{
if(n<=0)
return;
CurrentSize=0;
MaxSize=n;
heapArray=new T[MaxSize];
BuildHeap();
}
template<class T>
void MinHeap<T>::BuildHeap()
{
for (int i=CurrentSize/2-1; i>=0; i--)
SiftDown(i);
}
template<class T>
bool MinHeap<T>::isLeaf(int pos) const
{
return (pos>=CurrentSize/2)&&(pos<CurrentSize);
}
template<class T>
int MinHeap<T>::leftchild(int pos) const
{
return 2*pos+1; //返回左孩子位置
}
template<class T>
int MinHeap<T>::rightchild(int pos) const
{
return 2*pos+2; //返回右孩子位置
}
template<class T>
int MinHeap<T>::parent(int pos) const // 返回父节点位置
{
return (pos-1)/2;
}
template<class T>
void MinHeap<T>::SiftDown(int left)
{
//准备
int i=left; //标识父结点
int j=2*i+1; //标识关键值较小的子结点
T temp=heapArray[i]; //保存父结点
//过筛
while(j<CurrentSize)
{
if((j<CurrentSize-1)&&(heapArray[j]>heapArray[j+1]))
j++; //j指向右子结点
if(temp>heapArray[j])
{
heapArray[i]=heapArray[j];
i=j;
j=2*j+1;
}
else break;
}
heapArray[i]=temp;
}
template<class T>
void MinHeap<T>::SiftUp(int position)
{//从position向上开始调整,使序列成为堆
int temppos=position;
T temp=heapArray[temppos];
while((temppos>0)&&(heapArray[parent(temppos)]>temp))
{
heapArray[temppos]=heapArray[parent(temppos)];
temppos=parent(temppos);
}
heapArray[temppos]=temp;
}
template<class T>
bool MinHeap<T>::Insert(const T& newNode)
{//向堆中插入一个结点
if(CurrentSize==MaxSize)
return false;
heapArray[CurrentSize]=newNode;
SiftUp(CurrentSize);
CurrentSize++;
return true;
}
template<class T>
T& MinHeap<T>::RemoveMin()
{
if(CurrentSize==0)
{
cout<<"Can't Delete";
}
else
{
T temp=heapArray[0]; //取堆顶元素
heapArray[0]=heapArray[CurrentSize-1]; //堆末元素上升至堆顶
CurrentSize--;
if(CurrentSize>1)
SiftDown(0); //从堆顶开始筛选
return temp;
}
}
template<class T>
bool MinHeap<T>::Remove(int pos, T& node)
{// 删除给定下标的元素
if((pos<0)||(pos>=CurrentSize))
return false;
T temp=heapArray[pos];
heapArray[pos]=heapArray[--CurrentSize]; //指定元素置于最后
SiftUp(pos); //上升筛
SiftDown(pos); //向下筛
node=temp;
return true;
}
class ADJ
{
private:
int e; //边数
VNode *AdjList; //邻接表
int NNode; //顶点数
int MARK[MaxNode]; //访问标记数组
public:
ADJ(int itE,int itMaxNode)
{
e=itE;
NNode=itMaxNode;
AdjList=new VNode[NNode+1];
for(int i=0;i<NNode;i++)
{
AdjList[i].data=0;
AdjList[i].first=NULL;
}
}
~ADJ()
{
delete[] AdjList;
}
void CreateAdj()
{
int i,j;
int item;
struct Node* S;
for(int a=1;a<=NNode;a++)
{
cout<<"输入第"<<a<<"个顶点数据";
cin>>AdjList[a].data;
AdjList[a].first=NULL;
}
for(int k=0;k<e;k++)
{
cout<<"输入边<Vi,Vj>的顶点对序列号:";
cin>>i;
cin>>j;
cout<<"输入边的权:";
cin>>item;
S=new struct Node();
S->adjvex=j;
S->weight=item;
S->next=AdjList[i].first;
AdjList[i].first=S; //连接两个顶点
S=new struct Node();
S->adjvex=i;
S->weight=item;
S->next=AdjList[j].first;
AdjList[j].first=S;
}
}
void printAll()
{
cout<<"打印所有顶点的连接情况;"<<endl;
for(int i=1;i<=NNode;i++)
{
struct Node* temp=AdjList[i].first;
cout<<i;
cout<<"("<<AdjList[i].data<<")";
if(temp!=NULL)
cout<<"---"<<temp->weight<<"-->";
while(temp!=NULL)
{
cout<<temp->adjvex;
cout<<"("<<AdjList[temp->adjvex].data<<")"<<"--"<<temp->weight<<"-->";
temp=temp->next;
}
cout<<"---->NULL";
cout<<endl;
}
cout<<"输出了所有数据!"<<endl;
}
void AddEdgetoMST(Edge &E,Edge *MST,int tag)
{
MST[tag].from=E.from;
MST[tag].to=E.to;
MST[tag].weight=E.weight;
}
void Krushal();
};
void ADJ::Krushal()
{
Gentree A(NNode); //等价类
MinHeap<Edge> H(e); //最小值堆
struct Node *temp;
Edge * MST=new Edge[NNode-1]; //最小支撑树
int MSTtag=1; //最小支撑树的边的标记
Edge Ne;
for(int i=1;i<=NNode;i++) //将图的所有边插入最小值堆H中
{
temp=AdjList[i].first;
while(temp!=NULL)
{
if(i<temp->adjvex) //因为是无向图,所有应防止重复插入
{
Ne.from=i;
Ne.to=temp->adjvex;
Ne.weight=temp->weight;
H.Insert(Ne);
}
temp=temp->next;
}
}
int EquNum=NNode; //开始有|V|个等价类
while(EquNum>1) //合并等价类
{
Ne=H.RemoveMin(); //获得下一条权最小的边
if(Ne.weight==INFINITY)
{
cout<<"不存在最小支撑树:";
delete[] MST;
MST=NULL;
return ;
}
int from=Ne.from; //纪录该条边的信息
int to=Ne.to;
if(A.differ(from,to)) //如果边Ne的两个顶点不在一个等价类
{
A.UNION(from,to); //将边Ne的连个顶点在的两个等价类合并为一个
AddEdgetoMST(Ne,MST,MSTtag++); //将边e加到MST
EquNum--; //将等价类的个数减1
}
}
cout<<"输出无向图的最小支撑树的边和权:";
for(int i=1;i<NNode;i++)
cout<<MST[i].from<<"--"<<MST[i].to<<"("<<MST[i].weight<<")/t";
cout<<endl;
}
int main()
{
int E,MaxNode;
cout<<"输入无向图的顶点数和边数::";
cin>>MaxNode>>E;
ADJ T(E,MaxNode);
T.CreateAdj();
T.printAll();
cout<<endl;
cout<<"Kruskal算法的执行:"<<endl;
T.Krushal();
return 0;
}
注:本程序在VC2005中编译成功的。