`

邻接矩阵存储图-Prim 算法、Kruskal 算法、去边法

    博客分类:
  • C++
阅读更多

*********************************************************************************************************
 *
 * 程序完成的功能:输入并建立无向图的邻接矩阵,
 *       用 Prim 算法、Kruskal 算法以及去边法生成该图的最小代价生成树
 *
 * 作者:郭赞    

 *
 * 完成时间:2008年11月15日
 *
 * 程序说明:最大顶点数目初始为 10
 *    可选择自动生成邻接矩阵,也可自己输入
 *    一个顶点和自己之间的权值为 0
 *    没有直接连接的顶点之间的权值为 无穷大,即所能允许的最大值 Max_Num
 *
***********************************************************************************************************


#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;

#define Max_Size 10      //最大允许的顶点个数
#define Max_Num 65535     //系统允许的最大数
#define Wid 6       //输出数据的宽度
typedef char ElemType;     //顶点的数据类型

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
边的基本定义,w 为权值             *
typedef struct ArcCell{     
 int w;
} Arc, AdjMatrix[ Max_Size ][ Max_Size ];

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
对图的定义,vex[]中存储顶点,VexNum、ArcNum为顶点个数和边的条数*
typedef struct {      
 ElemType Vex[ Max_Size ];
 AdjMatrix Arcs;
 int VexNum;
 int ArcNum;
} Graph;

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
对边集数组的定义,在 prim算法,及去边法 del中使用    *
typedef struct {
 int fromvex;
 int endvex;
 int weight;
} Edge;


************************************************************
各函数的函数头
************************************************************

int CreatUDN ( Graph &G );    //建立邻接矩阵
void AutoCreat ( Graph &G );   //建立默认邻接矩阵
int LocatVex ( Graph G, ElemType V ); //确定顶点在Vex[]中的位置
void OutPut ( Graph G );    //输出邻接矩阵中的各条边
int GetChoice ();      //输入选择
void Prim ( Graph G );     
void Kru ( Graph G );
void Del ( Graph G );     //去边法
int SortEdge ( Graph G, Edge sort[] );
int Findpath ( Edge e[], int len, int from, int end , int mark );
void copy ( Edge hold[], Edge S[], int len);


*******************************************************************************
主函数,实现程序功能
*******************************************************************************

int main()
{
 Graph G;
 int choice = 0;

 if ( !CreatUDN ( G ) ) {
  cout << "创建邻接矩阵失败" <<endl;
  return 1;
 }

 OutPut ( G );
 while( choice != 4) {
  switch ( choice = GetChoice () ) {
   case 1 :  Prim ( G );  break;
   case 2 :  Kru ( G );   break;
   case 3 :  Del ( G );   break;
   case 4 :  break;
   default :
    cout << "错误的选择!!!\a" <<endl;
    break;
  }
 }

 return 0;
}

*******************************************************************************
函数:建立邻接矩阵,成功返回 1  
**调用者:main                 *

int CreatUDN ( Graph &G )
{
 int i = 0, j = 0, k = 0, weight = 0;
 char choice = 'Y';
 ElemType v1, v2;

 cout << "需要为你自动创建邻接矩阵吗 Y/N" << endl;
 cin >> choice;
 fflush ( stdin );
 //自动创建默认邻接矩阵
 if ( choice == 'Y' || choice == 'y') {
  AutoCreat ( G );
  return 1;
 }

 //读入顶点,创建顶点间的权值为默认值
 cout << "请输入顶点个数:" << endl;
 G.VexNum = cin.get()- '0';

 cout << "请输入各顶点数据:" <<endl;
 for ( i = 0; i <= G.VexNum-1; i++ )
  cin >> G.Vex[ i ];
 for ( i = 0; i <= G.VexNum-1; i++ )
  for ( j = 0; j <= G.VexNum-1; j++ )
  {
   if ( i == j )
    G.Arcs[ i ][ j ].w = 0;
   else
    G.Arcs[ i ][ j ].w = Max_Num;
  }

 //创建各条边
 fflush(stdin);
 cout << "请输入边的条数:" << endl;
 G.ArcNum = cin.get()-'0';

 cout << "请输入各边的数据(V1 V2 权值):" <<endl;
 for ( k = 0; k <= G.ArcNum-1; k++ ) {
  cin >> v1 >> v2 >> weight;
  //定位输入边的两个顶点的位置
  i = LocatVex( G , v1 );
  j = LocatVex( G , v2 );

  //输入的边的顶点数据有错误
  if( i == -1 || j == -1 ) {
   cout << "没有找到所要输入的边" <<endl;
   k--;
  }

  G.Arcs[ i ][ j ].w = weight;
  G.Arcs[ j ][ i ].w = weight;
 }
 
 return 1;
}

* 函数:在顶点数组 V[ ]中寻找所输入的数据 V,
  找到返回所在位置,否则返回 -1
 **调用者:手工创建邻接矩阵 CreatUDN  *

int LocatVex ( Graph G, ElemType V )
{
 int i = 0;
 for ( i = 0; i <= G.VexNum-1; i++ )
  if ( G.Vex[ i ] == V )
   return i;
 return -1;
}

*******************************************************************************
函数:自动建立邻接矩阵
      顶点分别为 a b c .....
   权值自动生成
**调用者:main                 *

void AutoCreat ( Graph &G )
{
 G.VexNum = Max_Size;
 for ( int i = 0; i <= G.VexNum-1; i++) {
  G.Vex[ i ] = 'A' + i;
  G.Arcs[ i ][ i ].w = 0;
 }

 //随机生成各条边之间的权值
 for ( int i = 0; i <= G.VexNum-1; i++)
  for ( int j = i+1; j <= G.VexNum-1; j++) {
   G.Arcs[ j ][ i ].w = G.Arcs[ i ][ j ].w = rand() % 50;
   //两点之间没有直接连接,权值为无穷大
   if ( G.Arcs[ j ][ i ].w == 0)
    G.Arcs[ j ][ i ].w = G.Arcs[ i ][ j ].w = Max_Num;
  }
}

*******************************************************************************
函数:输入选择
**调用者:main                 *

int GetChoice ()
{
 int choice = 0;

 cout << "请选择用何种算法求最小代价生成树:\n1-Prim 算法\n2-Kruskal 算法\n3-去边法\n4-退出" <<endl;
 cin >> choice;
 return choice;
}

*******************************************************************************
函数:输出邻接矩阵,以矩阵的格式输出
**调用者:main                 *

void OutPut ( Graph G )
{
 int i = 0, j = 0;

 cout << "邻接矩阵为:" <<endl;
 cout << setw ( Wid ) << " ";
 for ( i = 0; i <= G.VexNum-1; i++)
  cout << setw( Wid ) << G.Vex[ i ];
 cout <<endl;
 for ( i = 0; i <= G.VexNum-1; i++ ) {
  cout << setw( Wid ) << G.Vex[ i ];
  for ( j = 0; j <= G.VexNum-1; j++ ) {
   if ( G.Arcs[ i ][ j ].w == Max_Num )
    cout << setw( Wid ) << "*" ;
   else
    cout << setw( Wid ) << G.Arcs[ i ][ j ].w ;
  }
  cout << endl;
 }
}

*******************************************************************************
函数:prim算法求最小生成树
**调用者:main                 *

void Prim ( Graph G )
{
 // CT 中在第k个的时候,前 k-1个为已经放入最小树的顶点,后面的为没放入的
 Edge CT[ Max_Size ] ;
 Edge temp;
 int i = 0, j = 0, k = 0, w = 0, t = 0;

 //对应第0次的值,即和第一个顶点间的权值放入CT
 for ( i = 1; i < G.VexNum; i++) {
  CT [ i-1 ].fromvex = 0;
  CT [ i-1 ].endvex = i;
  CT [ i-1 ].weight = G.Arcs[ 0 ][ i ].w;
 }

 for ( k = 1; k < G.VexNum; k++) {
  //找到和已放入最小树的顶点的集合之间的权值最小的边
  int min = Max_Num;
  int m = k-1;
  for ( j = k-1; j < G.VexNum-1; j++)
   if ( CT[ j ].weight < min) {
    min = CT[ j ].weight;
    m = j;
   }

  //把最短边调换到 k-1位置
  temp = CT[ k-1 ];
  CT[ k-1 ] = CT[ m ];
  CT[ m ] = temp;

  //把新加入最小树的顶点值赋给 j
  j = CT [ k -1 ].endvex;

  //为下次求最小的边做准备,使树到数外各顶点之间的权值保持在最小
  for ( i = k; i < G.VexNum-1; i++) {
   t = CT[ i ].endvex;
   w = G.Arcs[ j ][ t ].w;
   //如果新加入的 j到某个顶点的权值小于目前的最小值,
   //将最小值更改为 j 到这个顶点的值,同时更改到这个顶点的树内顶点为 j
   if ( w < CT[ i ].weight) {
    CT[ i ].weight = w;
    CT[ i ].fromvex = j;
   }
  }
 }

 for ( i = 0; i < G.VexNum-1; i++ )
  if ( CT[ i ].weight == Max_Num ) {
   cout << "没有最小生成树" <<endl<<endl;
   return ;
  }

 for ( i = 0; i < G.VexNum-1; i++ ) {
  cout << setw( Wid ) << G.Vex[ CT[ i ].fromvex ]
  << setw( Wid ) << G.Vex [ CT[ i ].endvex ] << setw( Wid ) << CT[ i ].weight <<endl;
 }
}

*******************************************************************************
函数:kruskal算法求最小生成树
**调用者:main                 *

void Kru ( Graph G )
{
 //标记每个顶点所在的集合
 int  mark [ Max_Size ] = { 0 };
 int i = 0, j = 0, k = 0, temp1 = 0, temp2 = 0, sum = 0;
 int min = Max_Num, fromvex = 0, endvex  = 0, t = 0;

 //将所有顶点分属不同集合
 for ( i = 0; i <= G.VexNum-1; i++ )
  mark[ i ] = i;

 for ( t = 1; t <= G.VexNum-1; t++ ) {
  //找到权值最小的那条边,并且首尾顶点不在同一个集合
  min = Max_Num;
  for ( i = 0; i <= G.VexNum-1; i++ )
   for ( j = i+1; j <= G.VexNum-1; j++ )
    if ( mark[ i ] != mark[ j ] && G.Arcs[ i ][ j ].w < min ) {
      min = G.Arcs[ i ][ j ].w;
      fromvex = i;
      endvex = j;
    }

  if ( min != Max_Num ) {
   cout << setw( Wid ) << G.Vex[ fromvex ]
     << setw( Wid ) << G.Vex [ endvex ] << setw( Wid ) << min <<endl;
    sum ++;
  }

  //将首尾顶点所在的两个顶点集合合并为一个集合
  temp1 = mark [ endvex ];
  temp2 = mark [ fromvex ];
  for ( k = 0; k <= G.VexNum-1; k++) {
   if ( mark [ k ] == temp1 )
    mark [ k ] = temp2;
  }
 }
 if ( sum != G.VexNum-1 )
  cout << "没有最小生成树" <<endl<<endl;
}

*******************************************************************************
函数:去边法求最小生成树
**调用者:main                 *

void Del ( Graph G )
{
 Edge S[ Max_Size * Max_Size ], hold[ Max_Size * Max_Size ];
 int i = 0, j = 0, len = 0, temp = 0, sum = 0;

 //将 G 中的有效边放入Sort中,并且将其由大到小排序,返回边的条数
 len = SortEdge( G, S);

 //从最大权值边开始,依次检测能否去掉
 for ( i = 0; i <= len-1; i++ ) {
  temp = S[ i ].weight;
  //去掉这条边
  S[ i ].weight = Max_Num;
  //做一份S的拷贝hold
  copy ( hold, S, len);
  //去掉后图不在连通,回复
  if ( ! Findpath ( hold, len, hold[ i ].fromvex, hold[ i ].endvex, i ) )
   S[ i ].weight = temp;
 }

 for ( i = 0; i<= len-1; i++ )
  if ( S[ i ].weight != Max_Num ) {
   cout << setw( Wid ) << G.Vex[ S[ i ].fromvex ]<< setw( Wid )
   << G.Vex[ S[ i ].endvex ] << setw( Wid ) << S[ i ].weight <<endl;
   sum++;
  }

 if ( sum != G.VexNum-1 )
  cout << "没有最小生成树" <<endl<<endl;
}

*  函数:排序,将 G 中的有效边放入Sort中,
    并且由大到小排序,返回边的条数
 **调用者:去边法 Del    *

int SortEdge ( Graph G, Edge sort[] )
{
 int len = 0, i = 0, j = 0, pass = 0;
 Edge Hold;

 //将图中的有效边放入sort中,并计算出条数
 for ( i = 0; i <= G.VexNum-1; i++ )
  for ( j = i+1; j <= G.VexNum-1; j++ )
   if ( G.Arcs[ i ][ j ].w != Max_Num ) {
    sort[ len ].fromvex = i;
    sort[ len ].endvex = j;
    sort[ len ].weight = G.Arcs[ i ][ j ].w;
    len++;
   }

 //冒泡排序,从大到小
 for ( pass = 0; pass <= len-2; pass++ )
  for ( i = pass+1; i <= len-1; i++ )
   if ( sort[ pass ].weight < sort[ i ].weight ) {
    Hold = sort[ pass ];
    sort[ pass ] = sort[ i ];
    sort[ i ] = Hold;
   }
 return len;
}

*  函数:判断两点间是否有路径,有返回 1
 **调用者:去边法 Del     *

int Findpath ( Edge e[], int len, int from, int end, int mark)
{
 int i = 0, temp = 0;

 //此边已经使用过
 e[ mark ].weight = Max_Num;

 //找到路径
 if ( from == end )
  return 1;

 //依次搜索每条边,找到和此顶点相连接的有效边
 //继续以找到边的另一个顶点为基准搜索
 for ( i = 0; i < len; i++ )
  if ( e[ i ].weight != Max_Num ) {
   if ( e[ i ].fromvex == from )
    if ( Findpath ( e, len, e[ i ].endvex, end, i ))
     return 1;
   if ( e[ i ].endvex == from )
    if ( Findpath ( e, len, e[ i ].fromvex, end, i )) {
     return 1;
    }
  }
 return 0;
}

*  函数:把S拷进 hold
 **调用者:去边法 Del *

void copy ( Edge hold[], Edge S[], int len)
{
 for ( int i = 0; i < len; i++ )
  hold[ i ] = S[ i ];
}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics