您的位置:首页 > 其它

理论: 图论(8): 最小生成树

2016-01-02 16:00 260 查看

总括

给定一个无向图, 如果它的每个子图中的任意两点之间都相互连接并且是一棵树, 那么这棵树就叫做生成树。如果边上有权值, 那么使得边权值和最小的生成树叫做最小生成树

例如我们有这样一个图, 把顶点看作村庄, 边看作计划要修建的道路。 为了所有的村庄间的同通行, 恰好修建村庄的数目-1条道路时, 这就对应了一棵生成树。修建道路需要投入建设费, 那么求解使得道路建设费用最小的生成树就是最小生成树问题。

关于最小生成树, 常见的有prim算法和Kruskal算法, 这两种算法都是基于贪心策略的一种算法。很显然, 生成树是否存在和图是否联通是等价的,因此我们假定图是连通的。

prim算法

首先我们介绍prim算法。 prim算法和Dijkstra算法十分相似, 都是从某个顶点出发, 不断添加边的算法。

首先我们假设一棵只包含一个顶点S的树T。 然后贪心的选取T和其他顶点之间连接的最小权值的边, 并把它加到T中。 不断进行这的操作, 就可以得到一棵生成树。证明省略

·

·

废话不多说这个代码写得我心累

//prim队列优化版
//Q为优先队列 以边的长度从小到大排序
//node结构体     v代表到达的顶点 len代表道路的长度
//不定长数组G以邻接表的方式保存图
//mindist数组初始化为INF  oxfffffff
//intree初始化为false;
int Prim()
{
int sum = 0;
priority_queue<node>Q;
for (int i = 0; i < G[0].size(); i++)
{/*将与0号顶点相连的顶点放入队列*/
minDist[G[0][i].v] = G[0][i].len;
Q.push(G[0][i]);
}/*0号顶点已将在生成树之中*/
intree[0] = true;

while (!Q.empty())
{
node now = Q.top();
Q.pop();
if (intree[now.v])
continue;
intree[now.v] = true;
sum += now.len;
for (int i = 0; i < G[now.v].size(); i++)
{
int vex = G[now.v][i].v;
int len = G[now.v][i].len;
if (len < minDist[vex])
{/*如果顶点的距离len比现有的小, 更新, 压入队列*/
minDist[vex] = len;
Q.push(G[now.v][i]);
}
}
}
return sum;
}


优化之后的时间复杂度为0(E log V)

·

·

·

·

Kruskal算法

下面我们来介绍Kruskal算法。 Kruskal算法按照边的长度的顺序从小到大遍历一遍, 如果不产生圈或者重边就把这条边加入到最小生成树之中。

证明略;

接下来问题的重点就转化为了如何避免在添加边的时候造成圈或者是重边的形成: 并查集

不懂并查集?看这里

假设现在我们要把连接顶点U和W的边放入生成树中, 我们说要做的就是检查现在deU 和W是否在同一个联通分量之中。 如果在同一个连通分量之中, 那么添加之后就会形成环或者重边; 反之, 如果不在同一个连通分量之中, 那么添加之后不会形成圈和重边。

void init(int x)
{
for (int i = 1; i < x; i++)
{
father[i] = i;
rankk[i] = 0;
}
}

int find(int x)
{
int far = x;
while (far != father[far])
far = father[far];

int i = x;
while (father[i] != far)
{
int temp = father[i];
father[i] = far;
i = temp;
}
return far;
}

bool unoin(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
return false;

if (rankk[x] < rankk[y])
father[x] = y;
else
{
father[y] = x;
if (rankk[x] == rankk[y])
rankk[x]++;
}
return true;
}

bool cmp(node a, node b)
{
return a.price < b.price;
}

int kruskal(void)
{
int ans = 0;
sort(a, a + n, cmp);
for (int i = 1; i <= n; i++)
{
if (unoin(a[i].start, a[i].endd))
ans += a[i].price;
}
return ans;
}


重点!!!!!!

稠密图(边多点少)用Kruskal算法

稀疏图(点多边少)用prim算法

E >= V log V 的称为稠密图
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: