博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
算法导论——最小生成树
阅读量:5949 次
发布时间:2019-06-19

本文共 3146 字,大约阅读时间需要 10 分钟。

  对于一个连通图来说,我们可以去掉其中一些边依然保持其连通的性质,在这些图中存在一个或多个图,他们的路径总和是最小的,这样的图必然是树。因为,如果说图中存在环,则去掉环的一条边依然可以保证连通性,这与总路径和最小是矛盾的。这样的图被称为最下生成树。城市间铺设电路就可以利用最小生成树来进行规划。

如图所示的黑色路径构成了最小生成树,去掉bc并加入ah也是一棵最小生成树,可见一个图的最小生成树并不一定是唯一的。

  最小生成树可以使用安全边的策略进行生成:假设集合A是最小生成树的子集,我们可以找到一条边加入到A中,依然保持A是最小生成树的子集,这样的边就被称为安全边。为了寻找安全边,我们定义下方黑色部分ab与de边构成了集合A,(A,V-A)称为G的一个切割。如果一条边(v,u)一个端点属于A而另一个端点属于V-A,则称它为横跨切割(A,V-A)。在横跨切割的边中,可以找到一条或多条权重最小的边,该边称为轻量级边,轻量级边即是安全边。因为A一定要与V-A部分产生连接,这就必须要通过横跨切割的边,而轻量级边是其中最短的,所以必定属于最小生成树。下图所示ah bj bc cd df ef为横跨切割的边,其中cd为轻量级边。为了寻找轻量级边,有两种基于贪心策略的算法。

Kruskal算法

  Kruskal算法的思想是,寻找连接森林中两棵不同树的边里面的最短边作为安全边加入集合A。可以使用不相交集合来维护这样的结构,对每个结点建立一棵树。通过FIND-SET来返回结点属于哪棵树,有边加入集合A时合并u和v所在的树。时间复杂度可表示为O(ElgV)。

图中所示为各边按照长度顺序不断遍历检查是否要加入到集合A中,直到遍历完所有的边结束。

#include
#include
#include
using namespace std;#define SIZE 10class Node{public: int rank; Node *p;};class Road{public: int u; int v; int weight;};int G[SIZE][SIZE];//邻接矩阵,参数初始化略Node nodes[SIZE];void makeSet(Node x){ x.p = &x; x.rank = 0;}void Union(Node x,Node y){ if(x.rank > y.rank) y.p = &x; else{ x.p = &y; if(x.rank == y.rank) y.rank++; }}Node* findSet(Node x){ if(x.p != &x) x.p = findSet(*x.p); return x.p;}bool com (Road a,Road b) { return (a.weight
MSTKruskal(){ vector
A; int i,j; vector
roads; for(i = 0; i < SIZE; i++){ nodes[i] = *new Node(); makeSet(nodes[i]);//每棵树包含一个结点 } for(i = 0; i < SIZE; i++){ for(j = i + 1; j < SIZE; j++){ if(G[i][j] != 0){ Road road = *new Road(); road.u = i; road.v = j; road.weight = G[i][j]; roads.push_back(road); } } } sort(roads.begin(),roads.end(),com);//对所有路径进行降序排列 for(i = 0; i < roads.size(); i++){ Road road = roads[i]; if(findSet(nodes[road.u]) != findSet(nodes[road.v])){ A.push_back(road); Union(nodes[road.u],nodes[road.v]); } } return A;}

Prim算法

  Prim算法的思路是从根结点开始加入集合A,不断寻找A与V-A相连边中最短的,即横跨(A,V-A)的轻量级边。可以将V-A到A距离的最小值存储到优先队列Q中来减少每次遍历寻找最短边的时间。优先队列可以通过最小二叉堆或者斐波那契堆来实现,前者的渐进时间为O(ElgV)后者改进为O(E+VlgV)

#include
#include
using namespace std;#define SIZE 10#define INFI 10000class Road{public: int u; int v; int weight;};int G[SIZE][SIZE];//邻接矩阵,参数初始化略vector
MSTPrim(int root){ vector
A; int i; Road roads[SIZE];//记录到达A的最短路径 for(i = 0; i < SIZE; i++){ roads[i] = *new Road(); if(G[root][i] != 0){ roads[i].u = root; roads[i].v = i; roads[i].weight = G[root][i]; } else roads[i].weight = INFI; } while(A.size() != SIZE - 1){ int min = 0; for(i = 1; i < SIZE; i++){ if(roads[i].weight < roads[min].weight && roads[i].weight > 0) min = i;//寻找最短的路径 } A.push_back(roads[min]); roads[min].weight = -1;//表示该点已在A中 for(i = 0; i < SIZE; i++){ if(G[min][i] < roads[i].weight) roads[i].weight = G[min][i];//更新到达A的最短长度 } } return A;}

 

转载于:https://www.cnblogs.com/graywind/p/9438586.html

你可能感兴趣的文章
php __autoload作用
查看>>
python模块介绍-asynchat 异步socket命令/响应处理器
查看>>
域内删除帐号恢复
查看>>
在iPhone上实现内网抓包嗅探!
查看>>
使用 Dockefile 创建一个带有 ssh 的 ubuntu 镜像
查看>>
java中的匿名内部类总结
查看>>
oracle ebs 设置界面风格+显示颜色
查看>>
Python自动化开发学习5
查看>>
【首页】
查看>>
HTTPCLIENT总结
查看>>
HBase常用操作命令
查看>>
智造时代:HR会被机器人取代吗?
查看>>
商超大战解读:1号店与天猫超市,谁更有戏?
查看>>
我的友情链接
查看>>
《sed的流艺术之三》-linux命令五分钟系列之二十三
查看>>
我的助理辞职了
查看>>
7月学习工具
查看>>
ocserv服务器安装
查看>>
LVS-NAT实现Discuz负载均衡
查看>>
gnome 桌面 右击 open terminal 失效处理
查看>>