zoukankan      html  css  js  c++  java
  • Kruskal算法模拟讲解

    Kruskal 算法是一个求最小生成树的算法,即求最小的开销等

     算法可以这样,要求得最小生成树,那么n个节点只能包括n-1条边

     所以我们应该转换为寻找这最短的n-1条边,因此,可以先对所有的

     边进行从小到大排序,每次取出一条边来进行试探,看是否够成环,

     如果不构成环,那么肯定是最短的路径了,因为每次都是取最小

     的边来试探,最终可以求得最小的生成树代价和。

    /*
    	Filename:kruskal.cpp
    	Author: xiaobing
    	E-mail: xiaobingzhang29@gmail.com
    	Date: 2013-08-31
    */
    #include<iostream>
    #include<string>
    #include<string.h>
    #include<algorithm>
    #include<cstdlib>
    #include<list>
    #include<set>
    #include<vector>
    #define N 100
    #define INF 1000000
    using namespace std;
    
    /*
     Kruskal 算法是一个求最小生成树的算法,即求最小的开销等
     算法可以这样,要求得最小生成树,那么n个节点只能包括n-1条边
     所以我们应该转换为寻找这最短的n-1条边,因此,可以先对所有的
     边进行从小到大排序,每次取出一条边来进行试探,看是否够成环,
     如果不构成环,那么肯定是最短的路径了,因为每次都是取最小
     的边来试探,最终可以求得最小的生成树代价和。
    	用到的数据结构:
    		struct edge 表示一条边,包括两个端点及其代价
    		edge graph[N] 表示有N条边组成的图
    		int father[N] 表示每个点的最上层的根节点
    	解释:因为这里需要判断是否形成环路,可以这样,每添加一条
    	边,看两个点是否在已经添加进去的边的点集中,若对需要添加
    	的这条边,发现两个点都在之前的那个集合中,这一定会形成回
    	路,所以,这里设置一个数组father[N],起初时,每个值为-1,代
    	表每个点的根节点都没有(因为没有添加一条边进去),当添加一条
    	边后,如果他们的根节点不同,则设置大的那个点的父节点为小
    	的那个点,如x > y 则 father[x] = y,这样每个点都只有一个根,
    	或者没有根,为-1,所以对添加进的节点,都可以查出他的根,然后
    	做比较,都相同,说明已位于添加进的节点中了,否则把该边添加
    	进去。
       
     */
    
    //定义一条边
    struct edge{
    	int u;		//起始点
    	int v;		//目的点
    	int cost;	//两点之间的代价
    };
    
    //这是一个对块数排序算法调用的一个比较函数
    bool cmp(const edge &a, const edge &b){
    	return a.cost < b.cost;
    }
    
    //查找一个节点的根节点
    int findFather(int father[], int x){
    	//如果他的父节点不为-1,则应该递归,直到找到其父节点
    	if(father[x] != -1){
    		//将沿途的所有节点都指向同一个根节点
    		return father[x] = findFather(father, father[x]);
    	}
    
    	//若为-1,则该点就是根
    	return x;
    }
    
    //添加一条边
    bool unionEdge(int father[], int x, int y){
    	//找到一条边的两个端点的根节点
    	x = findFather(father, x);
    	y = findFather(father, y);
    
    	//根节点相同,说明已经加入了,再加入该边
    	//则会形成回路,该边舍弃,返回fasle
    	if(x == y){
    		return false;
    	}
    
    	//若不同,让大的节点的根节点指向小的节点
    	if(x > y)	father[x] = y;
    	if(x < y)	father[y] = x;
    
    	//该边可以加入,返回true
    	return true;
    }
    
    int main(){
    	edge graph[N];		//定义了一个包含N条边的图
    	int father[N];		//定义了一个包含N个节点的根节点
    	int i,j, n;	//n代表节点数
    	cin>>n;
    	//初始化数组
    	memset(graph, 0, sizeof(graph));
    	//初始化为-1表示任何点都没有父节点,即没有一条边已加入
    	memset(father, -1, sizeof(father));
    
    	int k = 0, cost, temp;
    
    	//接收数据
    	for(i = 0;i < n;i++)
    		for(j = 0;j < n;j++){
    			if(i > j){
    				graph[k].u = i;
    				graph[k].v = j;
    				cin>>cost;
    				//对于小于0的值,表示不可达,所以代价为无穷大INF
    				if(cost < 0){
    					graph[k].cost = INF;
    				} else {
    					graph[k].cost = cost;
    				}
    				k++;
    				continue;
    			}
    			//由于是对称的,该值无用,但得接收
    			cin>>temp;
    		}
    
    	//将所有边从小到大排序
    	sort(graph, graph + k, cmp);
    
    	//打印排序后的边
    	for(i = 0;i < k;i++){
    		cout<<i<<" "<<graph[i].u<<"->"<<graph[i].v<<": "<<graph[i].cost<<endl;
    	}
    
    	//count为记录已经加入的边数,到n-1时截止
    	//sum为最小生成树的代价和
    	int count = 0, sum = 0;
    
    	//从小到大遍历k条边
    	for(i = 0; i < k;i++){
    		//探测该边是否可加入
    		if(unionEdge(father, graph[i].u, graph[i].v)){
    			count++;
    			sum += graph[i].cost;
    		}
    
    		//当加入n-1条边时,已满足连通图,则退出
    		if(count == n - 1)	break;
    	}
    
    
    	cout<<"最小生成树代价和sum : "<<sum<<endl;
    
        return 0;
    }


    测试例子:

    7
    0 5 -1 -1 -1 11 2
    5 0 10 8 -1 -1 13
    -1 10 0 7 -1 -1 -1
    -1 8 7 0 12 9 4
    -1 -1 -1 12 0 10 -1
    11 -1 -1 9 10 0 3
    2 13 -1 4 -1 3 0


    结果:

    0 6->0: 2
    1 6->5: 3
    2 6->3: 4
    3 1->0: 5
    4 3->2: 7
    5 3->1: 8
    6 5->3: 9
    7 2->1: 10
    8 5->4: 10
    9 5->0: 11
    10 4->3: 12
    11 6->1: 13
    12 2->0: 1000000
    13 6->4: 1000000
    14 6->2: 1000000
    15 3->0: 1000000
    16 5->2: 1000000
    17 5->1: 1000000
    18 4->2: 1000000
    19 4->1: 1000000
    20 4->0: 1000000
    最小生成树代价和sum : 31
    



  • 相关阅读:
    经典问题的非经典解法
    经典问题之树的深度
    35、AndroidView的滑动方式
    30、Android属性动画
    31、Android矢量动画
    36、AndroidCanvas画布
    27、AndroidEventBus
    28、AndroidRxjava
    32、Android事件分发机制
    29、Android基本动画
  • 原文地址:https://www.cnblogs.com/riskyer/p/3293982.html
Copyright © 2011-2022 走看看