zoukankan      html  css  js  c++  java
  • 最小生成树算法:Kruskal算法 Prim算法

    定义

    对于连通的无向图G(V,E),如果一个E的无环子集T,可以连接所有节点,并且又具有最小权重,称树g(V,T)为图G(V,E)的最小生成树。

    概念

    伪代码

    Kruskal算法和Prim算法均使用贪心策略实现,两者的实现框架可由下列伪代码表示,首先,是一些叙述时使用的概念。
    集合A:某棵最小生成树的子集。
    **安全边: **加入集合A又不会破坏A性质的边。(在这里也即是,满足: 加入到A中,且能保证A依旧是某棵最小生成树的子集 该性质的边)
      begin
        A初始为空
        while(A未形成最小生成树)
            选择一条安全边。
            将安全边加入A。
      end

    算法正确性证明

    主要使用 循环不变式进行证明(这个概念在算法导论中经常用到)
    循环不变式:在每遍循环开始之前,A是某棵最小生成树的子集。
    初始化:集合A直接满足循环不变式
    保持:算法循环中,选择安全边保证了该性质。
    终止:当算法终止时,所有边均属于某棵最小生成树,所以算法正确。

    关于安全边

    预先的一些概念:
    切割:无向图G(V,E)的一个切割(S,V-S)是集合V的一个划分。
    横跨:如果边e属于E,且e的一个端点属于S,另一端点属于V-S,则该边横跨切割(S,V - S)
    尊重:如果一个E的子集A中不存在横跨切割(S,V - S)的边,则称该切割尊重集合A.
    轻量级边:在横跨一个切割的所有边中,权重最小的边称为轻量级边。
    定理:对于无向连通图G(V,E),A为E的子集且包含在在某可最小生成树中,设集合(S,V-S)为图G中尊重A的一个切割,若e为横跨切割(S,V-S)的一条轻量级边,则e对于集合A是安全的,即e为集合A的一条安全边。
    (下面的证明类似读书笔记,严谨的证明请参考算法导论)
    证明:假设轻量级边e两个端点u,v,(e横跨切割,所以u,v分别属于S,V - S),假设A包含在最小生成子树T(假设T不包含e)中,则 T中存在一条简单路径p由u到v,并且T 存在一条边e';属于该路径并且横跨该切割,现在删除e‘并且加入e形成另一个生成树T',因为权重e <= e'‘所以 权重T' <= T;因为T为最小生成树,所以T'也为最小生成树。即加入边e后集合A包含在最小生成树T'中,即边e对于集合A为安全边。
    推论:对于无向连通图G(V,E),A为E的子集且包含在在某可最小生成树中,C(Vc,Ec)为森林G‘(V,A)(包含G所有顶点,以及集合A中边,由A的定义,G'无环,且包含多个连通分量,由此构成森林G')中的一个连通分量,如果e连接C和其他连通分量的一条轻量级边,则e对集合A是安全的。
    证明:容易知道,切割(Vc,V - Vc)尊重A,由定理可得推论正确性。
    Kruskal算法:集合A为森林。寻找安全边的方式是,权重最小且连接两个连通分量(树)的边。
    Prim算法:集合A为树。寻找安全边的方式为,连接A与A之外节点的权重最小的边。

    Kruskal算法

    主要使用并查集来查询集合选择的边是否属于同一连通分量。
    伪代码
        A初始为空
        建立并查集
        按照权重对边进行升序排序。
        按顺序考察每条边:
            如果边端点分别属于不同连通分量,加入该边。

    代码实现

    主要实现了并查集(按秩合并 和 路径压缩)

    int a[101];
    int rk[101];
    void init_set()
    {
    	for(int i = 1; i <= 100; ++i)
    	{
    		a[i] = i;
    		rk[i] = 1;
    	}
    }
    int find_set(int x)
    {
    	int p = x;
    	while(a[p] != p)
    	{
    		p = a[p];
    	}
    	int now = x;
    	int tmp = 0;
    	while(now != p)
    	{
    		tmp = a[now];
    		a[now] = p;
    		now = tmp;
    	}
    	return p;
    }
    void merge(int xp ,int yp)
    {
    	if(rk[xp] > rk[yp])
    	{
    		a[yp] = xp;
    	}
    	else 
    	{
    		a[xp] = yp;
    		if(rk[xp] == rk[yp])
    		{
    			++rk[yp];
    		}
    	}
    }
    struct Nod
    {
    	int x,y,w;//分别表示边的端点x,y和边的权重w.
    	Nod():x(0),y(0),w(0){}
    	bool operator < (const Nod& tmp)const
    	{
    		return w < tmp.w;
    	}
    };
    int kruskal()
    {
    	init_set();
            int e = n*n;//边的数量;
    	sort(nod,nod + e);
    	int s1,s2;
    	for(int i = 1; i <= e; ++i)
    	{
    		s1 = find_set(nod[i].x);
    		s2 = find_set(nod[i].y);
    		if(s1 != s2)
    		{
    			merge(s1,s2);
    		}
    	}
    	return ret;
    }
    

    时间复杂度分析

        O(ElgV)
        详细分析暂时跳过:

    Prim 算法

    #define INF 0x7fffffff
    int in[101];
    struct Nod
    {
    	int id,key;//分别为节点的序号和集合A到该节点权重最小的边的权重。
    	Nod():id(0),key(0){}
    	bool operator <(const Nod& tmp)const
    	{
    		return key > tmp.key;
    	}
    };
    int key[101];//集合A到该节点的边的权重,不存在该边,设为无穷大。
    void prim(int r)
    {
    	for(int i = 1; i <= n; ++i)
    	{
    		key[i] = INF;
    		in[i] = 0;
    	}
    		
    	Nod tmp;
    	key[r] = 0;
    	tmp.id = r;
    	tmp.key = 0;
    	priority_queue<Nod> q;//使用优先队列维护待选择的节点。
    	q.push(tmp);
    	while(!q.empty())//操作1,取点
    	{
    		tmp = q.top();
    		q.pop();
    		if(key[tmp.id] < tmp.key)continue;
    		int id = tmp.id;
    		in[id] = 1;//标记为1,,标识属于最小生成树集合;
    
                     //操作2,考察边。
    		for(int i = 1; i <= n; ++i)//边数较少可使用邻接边实现,这里使用矩阵实现,
    		{
    			if(!in[i] && a[id][i] < key[i])
    			{
    				key[i] = a[id][i];
    				tmp.id =i;
    				tmp.key = key[i];
    				q.push(tmp);
    			}
    		}
    	}
    }
    

    时间复杂度分析

      
    操作1:使用优先队列实现,时间复杂度为lg(V)
    操作2:考察所有边,时间复杂度O(E)
    总的时间复杂度为:O(Elg(V));
    主要参考算法导论,如有错误,恳请指正

  • 相关阅读:
    谷歌AI中国中心成立,人工智能势不可挡?
    谷歌 AI 中国中心成立,人工智能势不可挡?
    谷歌 AI 中国中心成立,人工智能势不可挡?
    谷歌 AI 中国中心成立,人工智能势不可挡?
    Python将被加入高考科目?你怎么看?
    Python将被加入高考科目?你怎么看?
    Python将被加入高考科目?你怎么看?
    2833 奇怪的梦境
    奖金
    4040 EZ系列之奖金
  • 原文地址:https://www.cnblogs.com/lif323/p/9398718.html
Copyright © 2011-2022 走看看