贪心算法---最小生成树(Prim/Kruskal算法)---沟通无限校园网

  • Prim算法
  • Kruskal算法

A------Prim算法:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3fffffff; 
const int N=100;
bool s[N];
int closest[N];
int lowcost[N];

void Prim(int n,int u0,int c[N][N])//n-顶点个数,u0-开始顶点,c[N][N]-带权邻接矩阵;如果s[i]=true,说明顶点i已经加入最小生成树的顶点集合u,否则顶点i属于集合v-u,将最后的相关的最小权值传递到数组lowcost。 
{
s[u0]=true;//初始时,集合中u只有一个元素,即顶点u0
int i,j;
for(i=1;i<=n;i++)
{
	if(i!=u0)
	{
		lowcost[i]=c[u0][i];//除u0之外的顶点 
		closest[i]=u0;//最邻近点初始化为u0
		s[i]=false;//初始化u0之外的顶点不属于u集合,即属于v-u集合 
	}
	else
	    lowcost[i]=0; 
} 
for(i=1;i<=n;i++)
{
	int temp=INF;
	int t=u0;
	for(j=1;j<=n;j++)//在集合v-u中寻找距离集合u最近的顶点t 
	{
		if((!s[j])&&(lowcost[j]<temp))//!s[j]表示j结点在v-u集合中
		{
			t=j;
			temp=lowcost[j];
		} 
	}
	if(t==u0)//找不到t,跳出循环
	  break;
	s[t]=true;//否则,将t加入集合u
	for(j=1;j<=n;j++)//更新lowcost和closest
	{
		if((!s[j])&&(c[t][j]<lowcost[j]))//!s[j]表示j结点在v-u集合中,t到j的边值小于当前的最邻近值
		{
			lowcost[j]=c[t][j];//更新j的最近邻近值为t到j的边值
			closest[j]=t;//更新j的最邻近点为t 
		} 
	} 
}
}
int main()
{
	int n,c[N][N],m,u,v,w,u0;
	cout<<"输入结点数n和边数m:"<<endl;
	cin>>n>>m;
	int sumcost=0;
	for(int i=1;i<=n;i++)
	   for(int j=1;j<=n;j++)
	      c[i][j]=INF;
	cout<<"输入结点数u,v和边值w:"<<endl;
	for(int i=1;i<=m;i++)
	{
		cin>>u>>v>>w;
		c[u][v]=c[v][u]=w;
	}
	cout<<"输入任一结点u0:"<<endl;
	cin>>u0;
	//计算最后的lowcost的总和,即为最后要求的最小费用之和
	Prim(n,u0,c);
	cout<<"数组lowcost的内容为:"<<endl;
	for(int i=1;i<=n;i++)
	    cout<<lowcost[i]<<" ";
	cout<<endl;
	for(int i=1;i<=n;i++)
	   sumcost+=lowcost[i];
	cout<<"最小的花费是:"<<sumcost<<endl;
	return 0; 
} 

B------ Kruskal算法:

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100;
int nodeset[N];
int n, m;
struct Edge {
     int u;
     int v;
     int w;
}e[N*N];
bool comp(Edge x, Edge y) 
{
     return x.w < y.w;
}
void Init(int n)
{
     for(int i = 1; i <= n; i++)
          nodeset[i] = i;
}
int Merge(int a, int b)
{
     int p = nodeset[a];
     int q = nodeset[b];
     if(p==q) return 0;
     for(int i=1;i<=n;i++)//检查所有结点,把集合号是q的改为p
     {
       if(nodeset[i]==q)
          nodeset[i] = p;//a的集合号赋值给b集合号
     }
     return 1;
}
int Kruskal(int n)
{
     int ans = 0;
     for(int i=0;i<m;i++)
          if(Merge(e[i].u, e[i].v))
          {
              ans += e[i].w;
              n--;
              if(n==1)
                  return ans;
          }
     return 0;
}
int main()
{
  cout <<"输入结点数n和边数m:"<<endl;
  cin >> n >> m;
  Init(n);
  cout <<"输入结点数u,v和边值w:"<<endl;
  for(int i=0;i<m;i++)
      cin >> e[i].u>> e[i].v >>e[i].w;
  sort(e, e+m, comp);
  int ans = Kruskal(n);
  cout << "最小的花费是:" << ans << endl;
 return 0;
}

C------利用并查集来优化Kruskal算法:

#include <bits/stdc++.h>
using namespace std;
const int N = 100;
int father[N];
int n, m;
struct Edge {
     int u;
     int v;
     int w;
}e[N*N];
bool comp(Edge x, Edge y) {
     return x.w < y.w;//排序优先级,按边的权值从小到大
}
void Init(int n)
{
     for(int i = 1; i <= n; i++)
          father[i] = i;//顶点所属集合号,初始化每个顶点一个集合号
}
int Find(int x) //找祖宗
{
     if(x != father[x])
     father[x] = Find(father[x]);//把当前结点到其祖宗路径上的所有结点的集合号改为祖宗集合号
     return father[x]; //返回其祖宗的集合号
}
int Merge(int a, int b) //两结点合并集合号
{
     int p = Find(a); //找a的集合号
     int q = Find(b); //找b的集合号
     if(p==q) return 0;
     if(p > q)
           father[p] = q;//小的集合号赋值给大的集合号
     else
           father[q] = p;
     return 1;
}
int Kruskal(int n)
{
     int ans = 0;
     for(int i=0;i<m;i++)
          if(Merge(e[i].u, e[i].v))
          {
              ans += e[i].w;
              n--;
              if(n==1)
                  return ans;
          }
     return 0;
}
int main() 
{
    cout <<"输入结点数n和边数m:"<<endl;
    cin >> n >> m;
    Init(n);
    cout <<"输入结点数u,v和边值w:"<<endl;
    for(int i=0;i<m;i++)
        cin>>e[i].u>>e[i].v>>e[i].w;
    sort(e, e+m, comp);
    int ans = Kruskal(n);
    cout << "最小的花费是:" << ans << endl;
    return 0;
}

两种算法的比较
(1)从算法的思想可以看出,如果图G中的边数较小时,可以采用Kruskal算法,因为Kruskal算法每次查找最短的边;边数较多可以用Prim算法,因为它是每次加一个结点。可见,Kruskal算法适用于稀疏图,而Prim算法适用于稠密图。

(2)从时间上讲,Prim算法的时间复杂度为O(n2),Kruskal算法的时间复杂度为O(eloge)。

(3)从空间上讲,显然在Prim算法中,只需要很小的空间就可以完成算法,因为每一次都是从V−U集合出发进行扫描的,只扫描与当前结点集到U集合的最小边。但在Kruskal算法中,需要对所有的边进行排序,对于大型图而言,Kruskal算法需要占用比Prim算法大得多的空间。

发布了131 篇原创文章 · 获赞 58 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_43595030/article/details/104100214