Prim算法 Kruskal算法

本文详细介绍了两种经典的最小生成树算法——Prim算法和Kruskal算法。Prim算法通过贪心策略逐步构建树,每次选择离现有树最近的顶点;而Kruskal算法则按边的权重从小到大排序,逐个考虑是否添加到树中,避免形成环路。两种算法各有优劣,适用于不同场景。

一、Prim算法
1、要求:
(1)生成一颗连通的树
(2)生成树:包含全部顶点,V-1条边,没有回路,并且添加一条边会变成有回路
(3)权重和最小
2、过程模拟
最重要:贪心的思想,每一步都要选择权值最小的,这棵树所有跟顶点相连的边中最小的。从根节点开始,让树慢慢的长大。
过程:
这里写图片描述

从v1开始
这里写图片描述

跟v1有联系的是4,2,1,选择1,所以到了v4
这里写图片描述

现在可以选择的边有,跟v1相连接的4,2,跟v4连接的2,8,4,7,3,选择最小的2,选哪个都可以,这里选v2
这里写图片描述

现在有三个顶点了,v1有4,v2有10,v4有2,8,4,7,选择2,所以从v4连到v3
这里写图片描述
一直这样做,直到所有顶点访问完
3、思想
每次应该选择哪条边?用dist[]数组来解决。dist[]保存每个树外边的顶点vi到树顶点的集合V的最小的距离,还是用图演示。先从v1开始,树里边只有v1,所以各个顶点到树最短的距离dist[i],就是我们看到的4,1,2,还有一些没有连接上的(用无穷大表示)
这里写图片描述

接着把v4收进去,那么比如说v3到树的距离,就要更新,原来它到树的最短距离是4(就是到v1的距离),现在dist[3]变得更短了,变成了2(到v4最近)
这里写图片描述
就这样每次都更新出这个时刻,每个顶点跟树最近的距离,然后在这里距离中选取最短的那个边,把这个边上的顶点收进去

//不要看这个,这个是我的思路,估计只有我能看懂
从v0开始寻找
for(){
记录所有不是树的顶点到v0的距离
}
创建一个邻接表LGraph来保存树
while1){
//找这些跟树有关系的顶点中的最小的边
i=findMinDist_matric();
找不到 break 
InsertEdge();//找到就把边插进链表里
更新权值和、顶点是否访问、已收录顶点的信息

for(遍历每一个顶点){
    if(没有被访问,还有边){
        if(是否因为加入了一个结点,这个顶点更近树了){
        更新dist
        更新跟谁连接使他更近树了
        }
    }
}
}

if(所有的点都访问了){
输出权值和
}else{
不是树
}

4、代码

/*作用:邻接矩阵最小生成树
使用变量:用dist[]来描述树外边的顶点到树的最短距离、用 Vcount刻画收录了多少个顶点、
用parent[]描述,树外边的顶点到这棵树哪个顶点最近
更新:每次访问完一个结点,Vcount,dist[],parent都要更新*/
LGraph Prim_matric(MGraph Graph){
    LGraph MST;
    weightType dist[MaxVertexNum],totalWeight=0;
    Vertex parent[MaxVertexNum],i,j;
    int Vcount=0;//开始没有收录任何顶点
    Edge E;

    //初始化,从0这个顶点开始访问
    for(i=0;i<Graph->nv;i++){
        dist[i]=Graph->G[0][i];
        parent[i]=0;
    }
    dist[0]=0;//访问过的顶点,到这棵树的距离为0
    Vcount++;
    parent[0]=-1;//等于-1的是树根

    //用邻接表来承接树的信息
    //创建一个有顶点但是没有边的邻接表
    MST=CreateGraph_adjacencyList(Graph->nv);
    E=(Edge)malloc(sizeof(struct ENode));

    //一直遍历完所有的结点
    while(1){
        //寻找树外的结点到树的最短距离
        i=findMinDist_matric(Graph,dist);
        if(i==ERROR)
            break;
        E->v1=parent[i];
        E->v2=i;//连过去的结点
        E->weight=dist[i];
        InsertEdge_adjacencyList(MST,E);
        totalWeight+=dist[i];
        dist[i]=0;//标记成访问过
        Vcount++;

        //找是否能更新距离
        for(j=0;j<Graph->nv;j++){
            if(dist[j]!=0 &&Graph->G[i][j]<INFINITY){
                //未被收录进树+有边
                if(Graph->G[i][j]<dist[j]){
                    dist[j]=Graph->G[i][j];
                    parent[j]=i;
                }
            }
        }
    }
        if(Vcount<Graph->nv){
            totalWeight=ERROR;
            printf("不是最小生成树\n");
        }
            printf("最小树的权值和%d\n",totalWeight);
            return MST;
}

//寻找最小的权值点
Vertex findMinDist_matric(MGraph Graph,weightType dist[]){
    Vertex Minv,v;
    weightType MinDist=INFINITY;

    for(v=0;v<Graph->nv;v++){
        if(dist[v]!=0 &&dist[v]<MinDist){
            MinDist=dist[v];
            Minv=v;
        }
    }

    if(MinDist<INFINITY)
        return Minv;
    else return ERROR;
}

二、Kruskal算法
1、适用情景:边稀疏的情况
区别Prim:Prim收集的是顶点,Kruskal收集的是边
2、伪代码

viod Kruskal(){
    while(没到v-1条边 && 图中还有边){
        取出权重最小的边,从图中删除这条边(最小堆)
        if(这条边加入树中不构成回路,查并集判断)
            加入边
        else
            扔掉这条边
    }
    if(没有v-1条边)
   Error("不存在生成树");
}

3、代码

### 最大堆和最小堆 最大堆和最小堆是一种特殊的完全二叉树,属于堆数据结构。 #### 介绍 最大堆中每个节点的值都大于或等于其子节点的值,根节点的值是堆中的最大值;最小堆中每个节点的值都小于或等于其子节点的值,根节点的值是堆中的最小值。 #### 原理 堆的基本操作包括插入元素和删除堆顶元素。插入元素时,将元素添加到堆的末尾,然后通过上浮操作调整堆的结构,以满足堆的性质;删除堆顶元素时,将堆顶元素与堆的最后一个元素交换,然后删除最后一个元素,再通过下沉操作调整堆的结构。 #### 应用 常用于优先队列的实现,如任务调度,优先级高的任务可以优先处理;在图算法中,如Dijkstra算法Prim算法的优化实现,可用于快速找到最小(大)权值的边或节点。 ### Prim算法 #### 介绍 Prim算法是一种贪心算法,用于求解加权连通图的最小生成树(MST)。 #### 原理 从图中的任意一个顶点开始,将其加入到MST中,然后不断选择与MST中顶点相连的权值最小的边,并将该边的另一个顶点加入到MST中,直到所有顶点都被加入到MST中。迭代时,假设当前MST中顶点形成集合Vs,则对Vs中的每一个顶点,遍历与其相邻的所有边,并找到权值最小的边 [^3]。 #### 应用 在通信网络设计中,可用于确定连接所有节点的最小成本的线路布局;在电路设计中,用于找到连接所有电子元件的最短线路。 ### Kruskal算法 #### 介绍 Kruskal算法也是一种贪心算法,用于求解加权连通图的最小生成树。 #### 原理 将图中的所有边按照权值从小到大进行排序,然后依次选择权值最小的边,如果该边加入到MST中不会形成环路,则将其加入到MST中,直到MST中包含图中的所有顶点。难点在于判断加入边后是否会产生环路,可使用等价类来解决 [^1]。 #### 应用 在城市之间的道路建设规划中,可用于确定连接所有城市的最小成本的道路网络;在计算机网络中,用于构建最小成本的网络拓扑结构。 ```python # 以下是Prim算法的简单Python实现示例 import heapq def prim(graph): mst = [] visited = set() start_vertex = list(graph.keys())[0] visited.add(start_vertex) edges = [(weight, start_vertex, to) for to, weight in graph[start_vertex].items()] heapq.heapify(edges) while edges: weight, frm, to = heapq.heappop(edges) if to not in visited: visited.add(to) mst.append((frm, to, weight)) for to_next, weight_next in graph[to].items(): if to_next not in visited: heapq.heappush(edges, (weight_next, to, to_next)) return mst # 以下是Kruskal算法的简单Python实现示例 def kruskal(graph): edges = [] for u in graph: for v, weight in graph[u].items(): edges.append((weight, u, v)) edges.sort() parent = {node: node for node in graph} def find(node): if parent[node] != node: parent[node] = find(parent[node]) return parent[node] def union(node1, node2): root1 = find(node1) root2 = find(node2) if root1 != root2: parent[root1] = root2 return True return False mst = [] for weight, u, v in edges: if union(u, v): mst.append((u, v, weight)) return mst ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值