第一种,Prim算法.
算法:Prim算法和DIJ类似,整个代码也类似。
过程:假设顶点标号为1 ~ V,第一次先从所有未确定的点中找到距离1(此时只有1是确定的点)最近的点,然后将此点加入到已确定的点,并把该段距离加入总和。然后更新其他的边(这里和DIJ不同),每次更新,都是对已知的点进行选择,选择该点,与哪个已确定的点直接相连最短。然后在下次循环时,找到与确定的点(此时除了1还有另外一个点)最近的点,重复上述过程。
与DIJ的区别就在这里,DIJ的每次循环找的是与源点(一个顶点)最近的点(因为求最短路),而Prim的每次循环找的是与已经确定的点(一个集合)最近的点(但是不用刻意去与集合中的点一个个比较,因为在更新的方程里的每一次循环都能达到这个效果)。
附上Prim代码:
第二种,Kruskal算法.
算法:先对已知的边的长度进行排序(因为求最小生成树,只要看每两点的距离就好了,最后要加起来),然后从小到大依次遍历这些边,每次判断该边的两个端点是否已经在同个连通分量里了,在的话就continue(因为每条边只遍历一次,如果现在已经在同个连通分量里了,那么再加入这条边,就会形成圈),所以只有!same(e.u, e.v)才能继续加上该边的长度,并把这两个端点加入到同个连通分量。
配合算法:可以使用并查集来高效的判断是否属于同一个连通分量,来防止形成圈。 使用快速排序sort.
所以该算法的复杂度为:O(ElogV);
细节:当已经将V - 1条边加入到同个unite,那么就可以break了;因为一棵最小生成树最多就只有V - 1条边(V是结点数);说明已经形成了一棵树了。
算法:Prim算法和DIJ类似,整个代码也类似。
过程:假设顶点标号为1 ~ V,第一次先从所有未确定的点中找到距离1(此时只有1是确定的点)最近的点,然后将此点加入到已确定的点,并把该段距离加入总和。然后更新其他的边(这里和DIJ不同),每次更新,都是对已知的点进行选择,选择该点,与哪个已确定的点直接相连最短。然后在下次循环时,找到与确定的点(此时除了1还有另外一个点)最近的点,重复上述过程。
与DIJ的区别就在这里,DIJ的每次循环找的是与源点(一个顶点)最近的点(因为求最短路),而Prim的每次循环找的是与已经确定的点(一个集合)最近的点(但是不用刻意去与集合中的点一个个比较,因为在更新的方程里的每一次循环都能达到这个效果)。
附上Prim代码:
while (true)
{
int v = -1;
for (u = 1; u <= V; ++u)
{
if (!used[u] && (v == -1 || d[u] < d[v])) v = u;
}
if (v == -1) break;
used[v] = true; //表示把v点加入已经确定点的集合里
res += d[v];//总的最短路
for (u = 1; u <= V; ++u)
{
d[u] = min(d[u], cost[v][u]);//数组d表示该点与已经确定的点的最短距离(与DIJ表示的意义不同),更新它。
}
return res;
}
第二种,Kruskal算法.
算法:先对已知的边的长度进行排序(因为求最小生成树,只要看每两点的距离就好了,最后要加起来),然后从小到大依次遍历这些边,每次判断该边的两个端点是否已经在同个连通分量里了,在的话就continue(因为每条边只遍历一次,如果现在已经在同个连通分量里了,那么再加入这条边,就会形成圈),所以只有!same(e.u, e.v)才能继续加上该边的长度,并把这两个端点加入到同个连通分量。
配合算法:可以使用并查集来高效的判断是否属于同一个连通分量,来防止形成圈。 使用快速排序sort.
所以该算法的复杂度为:O(ElogV);
细节:当已经将V - 1条边加入到同个unite,那么就可以break了;因为一棵最小生成树最多就只有V - 1条边(V是结点数);说明已经形成了一棵树了。