最小生成树算法学习总结

一个有 n 个结点的 连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用 kruskal(克鲁斯卡尔)算法或 prim(普里姆)算法求出。

一、prim算法

由于prim算法每次都需要找出距离点集最近的点,故其时间复杂度与顶点数V有关,即为,以下是Prim算法的模板:

const int INF = 100000000;
const int maxv = 1000;
int n, G[maxv][maxv];//邻接矩阵存储图
bool vis[maxv];
int dis[maxv];
 
int prim(){
    fill(dis, dis + maxv, INF); 
    fill(vis, vis + maxv, 0); //初始默认所有点独立
    dis[0] = 0;//把点0加入集合
    for (int i = 0; i <= n; i++){
        int u = -1, MIN = INF;
        for (int j = 0; j <= n; j++){
            if (!vis[j] && dis[j] < MIN) { //如果这个点没有加入集合S,且到集合的距离更短
                MIN = dis[j];//更新点V到集合S的最小值
                u = j;//把点赋给u
            }
        }
        if (u == -1) return -1; //图不连通
        vis[u] = true;//如果找到了这个点,就把它加入集合S
        for (int v = 0; v <= n; v++) dis[v] = min(dis[v], G[u][v]);//用新加入的点更新dis[]
    }
}

二、Kruskal算法

Kuruskal算法由于需要判断一条边的两个顶点是否属于同一个连通块,这可以用并查集来判断,所以需要先补充并查集的知识

#define SIZE 13
int UFSets[SIZE];

void Initial(int S[]) {
    for (int i = 0; i < SIZE; i++)
        S[i] = -1;
}

int Find(int S[], int x) {
    int root = x;
    while (S[root] >= 0)root = S[root];
    //路径压缩
    while (x != root) {
        int t = S[x];
        S[x] = root;
        x = t;
    }
    return root;
}

void Union(int S[], int Root1, int Root2) {
    if (Root1 == Root2) return;
    if (S[Root2] > S[Root1]) { //Root2结点数更少
        S[Root1] += S[Root2];
        S[Root2] = Root1;
    } else {
        S[Root2] += S[Root1];
        S[Root1] = Root2;
    }
}

以下是Kruskal算法的模板,时间复杂度为:

要注意并查集的写法,不然容易造成死循环

const int maxv = 1000;
const int maxe = 300;
int UFSets[100]; //并查集大小
int n, G[maxv][maxv];//n个顶点
int Enum = 0;//边的总数

struct edge {
    int from;
    int to;
    int cost;
}E[maxe];

bool cmp(edge a, edge b) {
    return a.cost < b.cost;
}

int Find(int S[], int x) {
    int root = x;
    while (S[root] >= 0)root = S[root];
    //路径压缩
    while (x != root) {
        int t = S[x];
        S[x] = root;
        x = t;
    }
    return root;
}

int Kruskal() {
    int num_edge = 0; //n+1个顶点最小生成树应该有n条边
    fill(UFSets, UFSets + n + 1, -1); //fill(10):a[0]-a[9]
    sort(E, E + Enum, cmp);//所有边按权从小到大排序
    for (int i = 0; i < Enum; i++) {
        int a = Find(UFSets, E[i].from);
        int b = Find(UFSets, E[i].to);
        if (a != b) { //不在同一个连通块中 
            UFSets[a] = b; //将其放入同一个连通块中
            num_edge++;
            if (num_edge == n)break; //最小生成树创建完成
        }
    }
    if (num_edge != n) return -1; //图不连通返回-1 
    else return 1;
}

猜你喜欢

转载自blog.csdn.net/qq_21891843/article/details/129686944