無向圖的最小支撐樹Prim算法的實現

           無向圖的最小支撐樹Prim算法的實現
//主題:實現最小支撐樹的算法

//作者:Andyhou

//時間:2008年4月27日

//具體重要算法:
//             採用了最小堆來實現取最小邊,定義了一個邊的類。
//             Prim算法的具體實現。
//             頂點的存儲都是從1開始。
#include <iostream>
using namespace std;

const int MaxNode=100;
const int VISITED=1;
const int UNVISITED=0;

 

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

//最小值堆的建立
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 Prim(int s);
};


//最小支撐樹的實現
void ADJ::Prim(int s)
{
 int MSTtag=1;                  //最小支撐樹邊的標號,存儲都是從1開始的。
 struct Node* temp;
 Edge* MST=new Edge[NNode];
 Edge Ne;
 MinHeap<Edge> H(e);            //最小值堆
 
 for(int i=1;i<=NNode;i++)      //初識化標記數組
  MARK[i]=UNVISITED;
 int v=s;
 MARK[v]=VISITED;               //開始頂點首先被標記
 do{
  //將以v爲頂點,另一頂點未被標記的邊插入最小值堆H中。
  temp=AdjList[v].first;
  
  while(temp!=NULL)
  {
   if(MARK[temp->adjvex]==UNVISITED)
   {
    
    Ne.from=v;
    Ne.to=temp->adjvex;
    Ne.weight=temp->weight;
    H.Insert(Ne);
   }
   temp=temp->next;
  }
  //尋找下條權最小的邊
  bool Found=false;

  while(!H.empty())
  {

   Ne=H.RemoveMin();
   if(MARK[Ne.to]==UNVISITED)
   {
    
    Found=true;
    break;
   }
  }
  if(!Found)
  {
   cout<<"不存在最小支撐樹。"<<endl;
   delete []MST;
   MST=NULL;
   return ;
  }
  v=Ne.to;
  MARK[v]=VISITED;         //在頂點v的標記位上做已訪問的標記。

  AddEdgetoMST(Ne,MST,MSTtag++);  //將邊Ne加到MST中
 }while(MSTtag<NNode);   //注意是以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;
 int item;
 cout<<"輸入無向圖頂點數和邊數::";
 cin>>MaxNode>>E;
 ADJ T(E,MaxNode);
 T.CreateAdj();
 T.printAll();

 cout<<"輸入任意無向圖的一個點:";
 cin>>item;
 T.Prim(item);
 return 0;
}

 注:本程序在VC2005中編譯通過

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章