首页 > 编程语言 >最小生成树:Kruskal算法和Prim算法

最小生成树:Kruskal算法和Prim算法

时间:2024-03-25 22:44:23浏览次数:21  
标签:Prim Kruskal 最小 生成 算法 points 节点

首先区别一下图跟树:

树不会包含环,图可以包含环。
图的生成树其实就是在图中找一棵包含图中的所有节点的树。专业点说,生成树是含有图中所有顶点的无环连通子图。

最小生成树就是再所有可能的生成树中,权重和最小的那棵生成树就叫最小生成树(注意:最小生成树有n-1条边)。

Kruskal算法和Prim算法都是用于解决图的最小生成树(Minimum Spanning Tree,简称MST)问题的经典算法。在这个问题中,我们需要找到一个图中的最小生成树,即包含所有节点但总权重最小的子图(B站有up主做了很易懂的动画:【最小生成树(Kruskal(克鲁斯卡尔)和Prim(普里姆))算法动画演示】https://www.bilibili.com/video/BV1Eb41177d1?vd_source=dd65e5938f5f6dab5dc478dad590c5f9)。

Kruskal算法:

1. 概述:
Kruskal算法是一种贪婪算法,它通过不断选择边来构建最小生成树。它的基本思想是从小到大选择图中的边,并且保证所选边不构成环。

2. 算法步骤:

  • 初始化: 将所有的边按照权重从小到大进行排序。
  • 循环选择边: 从最小的边开始,依次考虑每一条边。
    • 如果当前边不会形成环(即加入该边后不会导致图中出现环),则选择该边加入最小生成树。
    • 如果形成环,则舍弃该边。
  • 重复步骤直至生成树包含了所有的节点。
    图片来自b站up主@WAY_zhong

3. 实现要点:

  • 使用并查集来判断是否形成环。

Prim算法:

1. 概述:
Prim算法也是一种贪婪算法,它从一个节点开始逐步构建最小生成树。与Kruskal算法不同,Prim算法是基于节点而不是边的选择来构建最小生成树。

2. 算法步骤:

  • 选择初始节点: 从图中任意一个节点开始。
  • 逐步扩展生成树:
    • 在当前的最小生成树集合和其余节点中选择一个最小权重的边,将其加入最小生成树。
    • 将新加入的节点加入到最小生成树的集合中。
  • 重复步骤直至生成树包含了所有的节点。

图片来自b站up主@WAY_zhong

3. 实现要点:

  • 使用优先队列来选择最小权重的边。
  • 用一个集合来记录已经加入最小生成树的节点,以避免重复选择。

例题引入

leetcode 第1584题 https://leetcode.cn/problems/min-cost-to-connect-all-points/description/
题目:给你一个points 数组,表示 2D 平面上的一些点,其中 points[i] = [xi, yi] 。

连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 :|xi - xj| + |yi - yj| ,其中 |val| 表示 val 的绝对值。

请你返回将所有点连接的最小总费用。只有任意两点之间 有且仅有 一条简单路径时,才认为所有点都已连接。
示例 1:

输入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
输出:20
解释:
我们可以按照上图所示连接所有点得到最小总费用,总费用为 20 。
注意到任意两个点之间只有唯一一条路径互相到达

Kruskal算法

思路:

  • 首先,遍历点集points,构建边,并将该边在points中的下标i,j和曼哈顿距离d存入arr
  • 由于Kruskal特点是先添加费用小的边,所以将arr按照曼哈顿距离从小到大排序
  • 利用并查集构建最小生成树。如果当前边的两个点没有不在最小生成树中,则将该边添加到最小生成树中,更新边数和费用,edge+=1,cost+=d,当边数edge==n-1时,说明构建完最小生成树。
点击查看代码
class Solution:
    def minCostConnectPoints(self, points: List[List[int]]) -> int:
        res = [] #存储每条边在points中的两个下标和曼哈顿距离
        n = len(points)
        # 计算任意两点之间的曼哈顿距离,并将边添加到边列表中
        for i in range(n):
            x1, y1 = points[i]
            for j in range(i+1,n):
                x2,y2 = points[j]
                res.append([i,j,abs(x2-x1)+abs(y2-y1)]) #i,j表示边的起点和终点
        #对边排序
        res.sort(key=lambda x:x[2])
        #初始化并查集
        parent = list(range(n))#列表存储了每个节点的父节点信息。
        #在最初的时候,每个节点的父节点都是自身,即parent[i] = i,表示每个节点自成一个集合。
        def find(x):
            if x != parent[x]:
                parent[x] = find(parent[x])
            return parent[x]
        #构建最小生成树
        edge = 0 #初始化边数为0
        cost = 0
        for i ,j ,d in res:
            a,b = find(i),find(j) 
            if a!= b: #每次考虑一条边,检查该边的两个端点是否已经在同一个集合中(即是否构成环)
                parent[b] = a
                edge += 1
                cost += d
            if edge == n-1: #结束条件(最小生成树边数为n-1)
                break
        return cost

Prim算法

思路:
初始化:

  • d列表用于表示各个顶点与加入最小生成树的顶点之间的最小距离,初始值设为无穷大。
  • vis列表表示是否已经加入到了最小生成树中,初始值设为False。

选择初始点:

  • 选取第一个点作为起始点,将其距离设为0,表示到自身的距离为0。

逐步扩展生成树:

  • 外层循环迭代每个点
  • 内层循环在未加入最小生成树的点中找到距离最小的点。
  • 将找到的点加入最小生成树,并更新距离列表。
  • 计算当前轮的最小距离,并加入到答案中。

返回结果:

  • 返回连接所有点的最小总费用ans。
点击查看代码
        n = len(points)
        d = [float("inf")]*n # 表示各个顶点与加入最小生成树的顶点之间的最小距离
        vis = [False]*n # 表示是否已经加入到了最小生成树里面
        d[0]= 0 # 选择第一个点作为起始点,将其距离设为0,表示到自身的距离为0
        cost = 0 # 最小生成树的总费用
        # 遍历每个顶点,将其加入到最小生成树中
        for _ in range(n): #也可替换成while循环
            # 寻找当前轮的最小d
            m = float("inf")
            node = -1  # 用于记录当前轮中距离最小的点
            
            for i in  range(n): 
            # 如果顶点i未加入到最小生成树,并且与已加入点之间的距离小于当前最小距离,则更新最小距离
                if not vis[i] and d[i] < m:
                    node = i
                    m = d[i]
            vis[node] = True # 将该点标记为已加入最小生成树
            cost += m # 更新最小生成树的总费用

            # 更新与加入的顶点相连接的顶点的d
            for i in range(n):
                 # 如果顶点i未加入到最小生成树,则更新其与加入顶点的距离
                if not vis[i]:
                    # 计算顶点i与加入顶点之间的曼哈顿距离,并更新距离列表d[i]
                    d[i] = min(d[i],abs(points[i][0]-points[node][0])+abs(points[i][1]-points[node][1]))
        return cost     

总结比较:

  • Kruskal算法:基于边的选择,适用于稀疏图,实现相对简单,使用并查集来检测环。
  • Prim算法:基于节点的选择,适用于稠密图,实现相对简单,使用优先队列来选择最小权重的边。

在选择算法时,可以根据具体的图的特点来决定使用哪种算法。如果图比较稀疏,即边数相对节点数较少,则Kruskal算法可能更有效率;而如果图比较稠密,即边数接近节点数的平方,则Prim算法可能更合适。

标签:Prim,Kruskal,最小,生成,算法,points,节点
From: https://www.cnblogs.com/taixian/p/18095594

相关文章

  • 蓝桥杯算法基础(29)字符串匹配(RabinKarp)(KMP)(前缀树,字典树,trie,后缀数组,高度数组)
     RabinKarpRabinKarpS:ABABABm个P:ABBn个1.朴素算法,挨个匹配2.哈希法hash->滚动哈希c0*31^2+c1*31^1+c2类似于进制的求法求hash值(c0*31+c1)*31+c2hash(p)=o(n)hash(s)=o(m*n)privatestaticvoidmatch(Stringp,Strings){longhash_p=hash(p);......
  • 加密算法概述:分类与常见算法
    码到三十五:个人主页心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得!在信息安全领域,加密技术是保护数据不被未授权访问的关键手段。Java作为一种广泛使用的编程语言,提供了丰富的加密API,支持多种加密算法。本文将介绍Java中加密算法的分类以及常见的......
  • 蓝桥杯算法集训 - Week 4:BFS、并查集、Flood Fill、哈希、单调栈/队列
    蓝桥杯算法集训-Week4本系列随笔用于整理AcWing题单——《蓝桥杯集训·每日一题2024》的系列题型及其对应的算法模板。一、BFSBFS算法复习参考:BFS(Java)广度优先搜索简单介绍、模板、案例(一)Ⅰ、代码模板staticvoidbfs(Troot){//双端队列,用来存储元素D......
  • 6种负载均衡算法
    6种负载均衡算法1、轮询法此算法将请求按顺序轮流的分配到后端服务器,他均衡的对待后台每一台服务器,而不关心服务器实际的连接数和当前的系统负载publicclassRoundRobin{privatestaticMap<String,Integer>serverWeightMap=newHashMap<String,Integer>();......
  • Java实现轮询调度算法(Round Robin)
    Java实现轮询调度算法(RoundRobin)Java实现轮询调度算法(RoundRobin)引言在计算机科学中,轮询调度算法(RoundRobin)是一种常见的任务调度算法。它被广泛应用于操作系统、网络路由器、负载均衡器等领域。本文将介绍轮询调度算法的原理、实现以及在Java中的应用。轮询调度算法原理......
  • 【每日算法】理论:AIGC模型 刷题:力扣链表操作
    上期文章【每日算法】理论:图像分割相关刷题:设计链表文章目录上期文章一、上期问题二、理论问题1、LAMAInpaint2、IPadapter模型3、Anydoor4、vit(VisionTransformer)架构5、MAE6、CLIP模型三、力扣刷题回顾-链表操作203.移除链表元素206.反转链表24.两两交换链表......
  • 学习笔记之算法快速排序
    快速排序听说有的公司面试会考?0.0快速排序思想:分治法基本思想:1、从数列中选出一个数2、分区(遍历),比它大的放他右边,比它小的或者等于的,放他左边3、对左右区间重复第2步,直到区间只有一个数(递归)参考:快速排序|菜鸟教程(runoob.com)在该网站......
  • 排序算法练习——最大间距:给定一个未排序的数组,找到排序后相邻元素之间的最大差值
    最大间距:给定一个未排序的数组,找到排序后相邻元素之间的最大差值。解决这个问题可以使用桶排序的思想。具体步骤如下:找到数组中的最大值和最小值。根据数组的长度,将数组划分成一定数量的桶,每个桶存放一定范围内的元素。计算每个桶内元素的最小值和最大值。遍历桶,计算相邻......
  • 排序算法练习——按照字符串的异位词分组:给定一个字符串数组,将所有异位词(字符相同但顺
    按照字符串的异位词分组:给定一个字符串数组,将所有异位词(字符相同但顺序不同的字符串)分组到同一个组中。要按照字符串的异位词分组,可以使用哈希表来将每个字符串排序后作为键,相同键的字符串即为异位词。以下是实现这个算法的Python代码:fromcollectionsimportdefaultdict......
  • 算法基础模板
    目录算法模版——基础篇1、整数二分2、浮点数二分3、高精度加法4、高精度减法5、高精度乘低精度6、高精度除以低精度7、一维前缀和8、二维前缀和9、一维差分10、二维差分11、位运算12、双指针算法13、离散化14、区间合并15、单链表16、双链表17、栈18、队列1.普通队列2.循环队列......