最小生成树-Prim算法和Kruskal算法
Prim算法
Prim
算法是一种产生最小生成树的算法。该算法于1930
年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník
)发现;并在1957
年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim
)独立发现;1959
年,艾兹格·迪科斯彻再次发现了该算法。
Prim
算法从任意一个顶点开始,每次选择一个与当前顶点集最近的一个顶点,并将两顶点之间的边加入到树中,从而找到最短路径
1. 在一个加权连通图中,顶点集合为V
,边集合为为E。
2从任意一个顶点开始,把它加入集合S,遍历所有的点到集合的距离,用dis[i]表示,dis[i]表示集合到第i个的点最小距离,遍历dis[i]找到集合S点最近的且不在集合S中的点,把找到的点加入集合S.
3遍历加入的点到所有不在集合S的点,如果加入的点到第i个点的距离比dis[i]上的少的话,更新dis[i];
4重复3的操作,直到所有点加入集合S.
代码
//mp[i][j]表示i到j的距离
for(int i=1;i<=k;i++) { dis[i]=mp[1][i];//把1加入集合,dis[i]表示1到i的距离 } vis[1]=1;//标记已加入集合 for(int i=2;i<=k;i++)//遍历剩下的点 { t=0; Min=inf;//初始化 for(int j=2;j<=k;j++) { if(!vis[j]&&dis[j]<Min)//如果j不在集合里,且是最小距离的点 { Min=dis[j];//更新最小值 t=j;//记录最小的点编号 } } vis[t]=1;//把找到的点加入集合 sum+=Min;//长度加Min for(int j=2;j<=k;j++)//更新dis[i] {
if (!vis[j]&&(mp[t][j]<dis[j]))//如果还没有加入集合,并且t到j的距离小于dis[j], { dis[j]=mp[t][j];//更新 } }
}
Kruskal算法
Kruskal是另一个计算最小生成树的算法,其算法原理如下。首先,将每个顶点放入其自身的数据集合中。然后,按照权值的升序来选择边。当选择每条边时,判断定义边的顶点是否在不同的数据集中。如果是,将此边插入最小生成树的集合中,同时,将集合中包含每个顶点的联合体取出,如果不是,就移动到下一条边。重复这个过程直到所有的边都探查过。
也就是用并查集来表示是否在同一个集合,再用排序找到最小的点;
代码:
struct node//定义一个结构体来储存相关信息 { int x; int y; int num; }a[maxn]; bool cmp(node A, node B)//排序 { return A.num < B.num; } int find(int root)//找到根节点 { int son = root, temp; while (root != pre[root]) { root = pre[root]; } while (son != root) { temp = pre[son]; pre[son] = root; son = temp; } return root; } int main() { scanf("%d%d", &n, &m);//n个顶点,m条边 for (int i = 1; i <= n; i++)//初始化并查集数组 { pre[i] = i; } for (int i = 1; i <= m; i++)//读入m条边 { scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].num); } sort(a + 1, a + m + 1, cmp);//排序 for (int i = 1; i <= m; i++) { fx = find(a[i].x); fy = find(a[i].y); if (fx != fy)//这两个点不在同一集合,也就是没有连通 { pre[fx] = fy;//连起来 sum += a[i].num; } } }