Prim 算法
算法理论
Prim算法的思想
1)清空生成树,任取一个顶点加入生成树;
2)在那些一个端点在生成树里,另一个端点不在生成树里的边中,选取一条权最小的边,将它和另一个端点加进生成树;
3)重复步骤2,直到所有的顶点都进入了生成树为止,此时的生成树就是最小生成树。
Dijkstra
算法和Prim
算法实际上是相同的思路,不过是数组d[]
所表示的最小距离含义不同而已;其中,Dijkstra
表示为起点s
到达顶点Vi
的最短距离,而Prim
表示为顶点Vi
与集合S
的最短距离。
伪代码
//伪代码
Prime(G, d[]){
初始化;
for(循环n次){
u = 使d[u]最小的还未被访问的顶点的标号;
记u已被访问;
for(从u出发能到达的所有顶点){
if(v未被访问 && 以u为中介点使得v与集合S的最短距离d[v]更优){
将G[u][v]赋值给v与集合的最短距离d[v];
}
}
}
}
邻接矩阵版
int n, G[MAXV][MAXV]; //n为顶点数
int d[MAXV]; //*****顶点与集合S的最短距离*****
bool vis[MAXV] = {false}; //标记数组
int prime(int s){ //默认0为初始点,函数返回最小生成树的边权之和
fill(d, d+MAXV, INF);
d[s] = 0; //只有0号顶点到集合S的距离为0,其余全为INF
int ans = 0; //存放最小生成树的边权之和
for(int i=0; i<n; i++){
int u = -1, MIN = INF;
for(int j=0; j<n; j++){
if(vis[j] == false && d[j] < MIN){
u = j;
MIN = d[j];
}
}
if(u == -1) return -1;//找不到小于INF的d[u],则剩下的顶点和集合S不连通
vis[u] = true;
ans += d[u]; //*****将与集合S最小的边加入最小生成树*****
for(int v=0; v<n; v++){
if(vis[v] == false && G[u][v]!=INF && G[u][v] < d[v]){
d[v] = G[u][v]; //(***注意这里与Dijkstra的区别***)
}
}
}
return ans;
}
邻接表版
struct node{
int v, dis; //v为边的目标结点,dis为边权
};
vector<node> Adj[maxn]; //邻接表; Adj[u]保存从u出发能到达的所有顶点(结构体中还保存了其间的边权)
//邻接表版本
void Dijkstra(int s) {
fill(d, d + maxn, INF);
d[s] = 0;
int ans = 0;
for(int i = 0; i < n; i++) {
int u = -1, MIN = INF;
for(int j = 0; j < n; j++) {
if(vis[j] == false && d[j] < MIN) {
u = j;
MIN = d[j];
}
}
if(u == - 1) return;
vis[u] = true;
ans += d[u];
for(int j = 0; j < Adj[u].size(); j++){ //注意vector<>保存的是结构体
int v = Adj[u][j].v;
if(vis[v] == false && Adj[u][j].dis < d[v]){
d[v] = Adj[u][j].dis;
}
}
}//for - i
}//Dijkstr
应用练习
Kruskal算法
算法理论
算法思想:
1.对所有边进行从小到大的排序。
2.每次选一条边(最小的边),如果如果形成环,就不加入(u,v)中,否则加入。那么加入的(u,v)一定是最佳的。
代码
struct edge{
int u, v;
int cost;
}E[MAXE];
bool cmp(edge a, edge b){
return a.cost < b.cost;
}
int father[N]; // 并查集数组
int findFather(int x){
...
}
int kruskal(int n, int m){
int ans=0, Num_Edge = 0;
for(int i = 1; i <= n; i++){
father[i] = i;
}
sort(E, E+m, cmp);
for(int i = 0; i < m; i++){
int faU = findFather(E[i].u);
int faV = findFather(E[i].v);
if(faU != faV){
father[faU] = faV;
ans += E[i].cost;
Num_Edge++;
if(Num_Edge == n - 1)
break;
}
}
if(Num_Edge != n - 1)
return -1;
else
return ans;
}
两种算法比较
prim算法适合稠密图(边多),kruskal算法适合稀疏图(边少)。