最小生成树

Prim 算法

算法理论

Prim算法的思想

1)清空生成树,任取一个顶点加入生成树;
2)在那些一个端点在生成树里,另一个端点不在生成树里的边中,选取一条权最小的边,将它和另一个端点加进生成树;
3)重复步骤2,直到所有的顶点都进入了生成树为止,此时的生成树就是最小生成树。

Dijkstra算法和Prim算法实际上是相同的思路,不过是数组d[]所表示的最小距离含义不同而已;其中,Dijkstra表示为起点s到达顶点Vi的最短距离,而Prim表示为顶点Vi与集合S的最短距离。

伪代码

//伪代码
Prime(G, d[]){
    初始化;
    for(循环n次){
        u = 使d[u]最小的还未被访问的顶点的标号;
        记u已被访问;
        for(从u出发能到达的所有顶点){
            if(v未被访问 && 以u为中介点使得v与集合S的最短距离d[v]更优){
                将G[u][v]赋值给v与集合的最短距离d[v];
            }
        }
    }
}

邻接矩阵版

int n, G[MAXV][MAXV];  //n为顶点数
int d[MAXV];  //*****顶点与集合S的最短距离*****
bool vis[MAXV] = {false}; //标记数组
int prime(int s){ //默认0为初始点,函数返回最小生成树的边权之和
    fill(d, d+MAXV, INF);
    d[s] = 0;  //只有0号顶点到集合S的距离为0,其余全为INF
    int ans = 0;  //存放最小生成树的边权之和
    for(int i=0; i<n; i++){
        int u = -1, MIN = INF;
        for(int j=0; j<n; j++){
            if(vis[j] == false && d[j] < MIN){
                u = j;
                MIN = d[j];
            }
        }
        if(u == -1) return -1;//找不到小于INF的d[u],则剩下的顶点和集合S不连通
        vis[u] = true;
        ans += d[u];   //*****将与集合S最小的边加入最小生成树*****
        for(int v=0; v<n; v++){ 
            if(vis[v] == false && G[u][v]!=INF && G[u][v] < d[v]){
                d[v] = G[u][v];  //(***注意这里与Dijkstra的区别***)
            }
        }
    }
    return ans;
}

邻接表版

struct node{
    int v, dis; //v为边的目标结点,dis为边权
};
vector<node> Adj[maxn]; //邻接表; Adj[u]保存从u出发能到达的所有顶点(结构体中还保存了其间的边权)

//邻接表版本
void Dijkstra(int s) {
    fill(d, d + maxn, INF);
    d[s] = 0;
    int ans = 0;
    for(int i = 0; i < n; i++) {
        int u = -1, MIN = INF;
        for(int j = 0; j < n; j++) {
            if(vis[j] == false && d[j] < MIN) {
                u = j;
                MIN = d[j];
            }
        }
        if(u == - 1) return;
        vis[u] = true;
      	ans += d[u];
        for(int j = 0; j < Adj[u].size(); j++){ //注意vector<>保存的是结构体
            int v = Adj[u][j].v; 
            if(vis[v] == false && Adj[u][j].dis < d[v]){
              	d[v] = Adj[u][j].dis;
            }
        }
        
    }//for - i
}//Dijkstr

应用练习

还是畅通工程(浙江大学)

继续畅通工程(浙江大学)

Kruskal算法

算法理论

算法思想:

1.对所有边进行从小到大的排序。

2.每次选一条边(最小的边),如果如果形成环,就不加入(u,v)中,否则加入。那么加入的(u,v)一定是最佳的。

代码

struct edge{
	int u, v;
  int cost;
}E[MAXE];

bool cmp(edge a, edge b){
	return a.cost < b.cost;
}

int father[N]; // 并查集数组
int findFather(int x){
	...
}

int kruskal(int n, int m){
	int ans=0, Num_Edge = 0;
	for(int i = 1; i <= n; i++){
		father[i] = i;
	}
	sort(E, E+m, cmp);
	for(int i = 0; i < m; i++){
		int faU = findFather(E[i].u);
		int faV = findFather(E[i].v);
		if(faU != faV){
			father[faU] = faV;
			ans += E[i].cost;
			Num_Edge++;
			if(Num_Edge == n - 1)
				break;
		}
	}
  if(Num_Edge != n - 1)
  	return -1;
  else
  	return ans;
}

两种算法比较

prim算法适合稠密图(边多),kruskal算法适合稀疏图(边少)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值