`

邻接矩阵存储图-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 ];
}

分享到:
评论

相关推荐

    cpp-图论算法最小生成树Prim算法和Kruskal算法C实现

    C语言实现Prim算法时,可以使用邻接矩阵或优先队列(如二叉堆)来存储边的权重和查找最短边。邻接矩阵适合表示稠密图,而优先队列更适合稀疏图。 ### Kruskal算法 Kruskal算法同样基于贪心策略,但它的核心是按照...

    Matlab版prim Kruskal算法实现文档

    在Matlab中实现Prim算法和Kruskal算法可以使用邻接矩阵表示图,分别保存为文件Graph1.m和Graph2.m。然后,在主程序finallyprim中直接调用这些文件,并使用sprintf格式化字符串的方法,避免了编程的人每次根据输入图...

    Prim 算法、Kruskal 算法和去边法求无向图的最小代价生成树

    输入无向图的邻接矩阵,使用Prim 算法、Kruskal 算法和去边法三种算法求该图的最小代价生成树,并分析各自的时间复杂度。

    C++实现图的存储、Prim和Kruskal算法

    本项目将深入探讨如何用C++通过邻接矩阵来存储图,并实现两种经典算法:Prim算法和Kruskal算法,用于构建最小生成树。 首先,我们来看一下邻接矩阵的存储方式。邻接矩阵是一个二维数组,数组的大小通常是n×n,其中...

    用Prim和Kruskal算法构造最小生成树

    虽然示例代码中没有给出完整的Kruskal算法实现,但是可以推测其大致流程应该包括定义边结构体、创建图、读取图的信息、排序边以及检查环路等步骤。 #### 邻接矩阵表示法 在这个示例中,图是以邻接矩阵的形式存储的...

    最小生成树 prim算法和Kruskal算法实现

    prim算法 Kruskal算法分别实现最小生成树

    Prim算法与Kruskal算法求最小生成树

    Prim算法更适合处理稠密图,即边的数量接近于顶点数量的平方,因为它主要以顶点为中心扩展;而Kruskal算法则适用于稀疏图,即边的数量远小于顶点数量的平方,因为它主要考虑边的顺序。在实际应用中,根据图的特性...

    2018211302班-2018210074-熊宇-Prim VS Kruskal1

    Kruskal算法的正确性则可以通过反证法来证明,若在某一步加入的边会形成环,则说明已经找到了一个更小的生成树,这与已知的最小生成树是最小的矛盾,因此Kruskal算法每次选择的边都不会形成环,保证了算法的正确性。...

    定义采用邻接矩阵存储的图结构封装DFS、BFS算法

    定义采用邻接矩阵存储的图结构封装DFS、BFS算法

    数据结构邻接矩阵DFS非递归算法以及PRIM算法最小生成树

    PRIM算法与Kruskal算法一样,都是解决最小生成树问题的经典方法,但它们在选择边的方式上有所不同,Kruskal算法是按照边的权重从小到大选择,而PRIM算法是从节点出发逐步扩展。 总结来说,邻接矩阵在图数据结构中...

    Prim算法和Kruskal算法的Matlab实现[归纳].pdf

    但是,Prim算法的时间复杂度为O(n^2),而Kruskal算法的时间复杂度为O(ElogE),其中E为图中的边数。因此,在大规模图中,Kruskal算法的效率更高。 六、 结论 Prim算法和Kruskal算法都是常用的最小生成树算法,它们...

    C++ Prim算法Kruskal算法构造可以使n个城市连接的最小生成树

    (1)、实验题目:给定一个地区的n 个城市间的距离网,用Prim算法或Kruskal算法建立最小生成树,并得到的最小生成树的代价。 (2)、实验要求: 1、城市间的距离网采用的邻接矩阵表示,邻接矩阵的存储结构定义采用...

    prim和kruskal算法求最小生成树

    Prim算法适合稠密图,因为它每次迭代都考虑了所有相邻顶点,而Kruskal算法则更适合稀疏图,因为它的主要时间消耗在于边的排序。在实现上,Prim算法通常使用优先队列优化查找最小边的过程,而Kruskal算法依赖于并查集...

    数据结构 图的最小生成树 C++描述 使用prim算法、kruskal算法

    - 在C++实现中,Prim算法通常使用优先队列(如二叉堆)来快速找到最小权重的边,以及邻接矩阵或邻接表来存储图的信息。 3. **Kruskal算法**: - Kruskal算法也是贪心算法,但其策略是从图中最小的边开始选择,并...

    Prim与Kruskal算法的最小生成树matlab实现

    在MATLAB中,可以使用邻接矩阵或邻接表数据结构来实现Prim算法。具体步骤包括: - 初始化:选择一个起始顶点,将其加入最小生成树。 - 循环:对于当前树中每个顶点,找出与非树顶点的最短边,将对应的顶点加入树中...

    头歌数据结构图的最小生成树算法

    // 该函数使用邻接矩阵存储结构,通过Prim算法构建最小生成树 } ``` ##### 2.2.2 邻接表存储 ```c void MiniSpanTree_PRIM(ALGraph G, VertexType u) { // 代码实现省略,参见给定文件 // 该函数使用邻接表存储...

    python最小生成树-Prim算法和Kruskal算法.docx

    prim算法求最小生成树

    Prim+Kruskal 算法求最小生成树.zip_c语言 算法

    - 图的表示:可能使用邻接矩阵或邻接表来存储图的结构。 - Prim算法的实现:从一个顶点开始,逐步扩展最小生成树。 - Kruskal算法的实现:先对所有边进行排序,然后检查每条边是否构成环路。 - 辅助数据结构:如优先...

Global site tag (gtag.js) - Google Analytics