Kruskal 算法 详解

news2024/11/22 20:51:58

Kruskal算法详解

Kruskal算法是一种用于解决**最小生成树(MST,Minimum Spanning Tree)**问题的经典贪心算法。最小生成树是一个无向连通图的一个子图,该子图包含所有节点且边的权值总和最小,并且不包含环。


1. Kruskal算法的基本思想

  1. 贪心策略: 每次选择权值最小的边,且保证加入这条边后不会形成环。
  2. 步骤:
    • 将图中的边按照权值从小到大排序。
    • 从权值最小的边开始,依次尝试加入生成树中。
    • 如果加入某条边后不会形成环,则将该边加入生成树。
    • 重复直到生成树包含 V-1 条边(V 是图的节点数)。

2. Kruskal算法的主要步骤

假设有一个图 G = ( V , E ) G = (V, E) G=(V,E),其中 V V V 是顶点集合, E E E 是边集合。

步骤 1:边排序

将图中所有的边按照权值从小到大排序。

步骤 2:初始化

  • 创建一个并查集(Union-Find),用于检测环。
  • 初始化生成树的边集合为空。

步骤 3:依次处理边

按照边的权值顺序,逐条检查:

  1. 如果当前边的两个端点属于不同的连通分量(即不在同一个集合中),则可以安全地加入生成树。
  2. 将这两个端点合并到同一个连通分量(使用并查集实现)。
  3. 如果当前边的两个端点已经在同一个连通分量中,跳过该边,避免形成环。

步骤 4:停止条件

当生成树包含 V-1 条边时,算法结束。


3. Kruskal算法的实现

以下是一个用Java实现Kruskal算法的完整代码。

Java代码实现

import java.util.*;

class Edge implements Comparable<Edge> {
    int src, dest, weight;

    // 构造函数
    public Edge(int src, int dest, int weight) {
        this.src = src;
        this.dest = dest;
        this.weight = weight;
    }

    // 按权值排序
    public int compareTo(Edge other) {
        return this.weight - other.weight;
    }
}

class KruskalAlgorithm {
    // 找到集合的根节点(路径压缩优化)
    public static int find(int[] parent, int node) {
        if (parent[node] != node) {
            parent[node] = find(parent, parent[node]);
        }
        return parent[node];
    }

    // 合并两个集合(按秩合并优化)
    public static void union(int[] parent, int[] rank, int u, int v) {
        int rootU = find(parent, u);
        int rootV = find(parent, v);

        if (rootU != rootV) {
            if (rank[rootU] > rank[rootV]) {
                parent[rootV] = rootU;
            } else if (rank[rootU] < rank[rootV]) {
                parent[rootU] = rootV;
            } else {
                parent[rootV] = rootU;
                rank[rootU]++;
            }
        }
    }

    // Kruskal算法实现
    public static List<Edge> kruskal(int V, List<Edge> edges) {
        // 按权值升序排序所有边
        Collections.sort(edges);

        // 初始化并查集
        int[] parent = new int[V];
        int[] rank = new int[V];
        for (int i = 0; i < V; i++) {
            parent[i] = i;
            rank[i] = 0;
        }

        // 结果存储生成树的边
        List<Edge> mst = new ArrayList<>();

        // 遍历边
        for (Edge edge : edges) {
            int srcRoot = find(parent, edge.src);
            int destRoot = find(parent, edge.dest);

            // 如果不在同一个连通分量,则加入生成树
            if (srcRoot != destRoot) {
                mst.add(edge);
                union(parent, rank, srcRoot, destRoot);

                // 如果边数达到 V-1,停止
                if (mst.size() == V - 1) {
                    break;
                }
            }
        }

        return mst;
    }

    public static void main(String[] args) {
        int V = 4; // 顶点数
        List<Edge> edges = new ArrayList<>();

        // 添加边 (src, dest, weight)
        edges.add(new Edge(0, 1, 10));
        edges.add(new Edge(0, 2, 6));
        edges.add(new Edge(0, 3, 5));
        edges.add(new Edge(1, 3, 15));
        edges.add(new Edge(2, 3, 4));

        // 运行Kruskal算法
        List<Edge> mst = kruskal(V, edges);

        // 输出最小生成树
        System.out.println("Edges in the MST:");
        for (Edge edge : mst) {
            System.out.println("src: " + edge.src + ", dest: " + edge.dest + ", weight: " + edge.weight);
        }
    }
}

4. 输出结果

假设输入图的边为:

(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)

Kruskal算法的输出为:

Edges in the MST:
src: 2, dest: 3, weight: 4
src: 0, dest: 3, weight: 5
src: 0, dest: 1, weight: 10

对应的最小生成树包含的边是 (2, 3), (0, 3), (0, 1),总权值为 4 + 5 + 10 = 19 4 + 5 + 10 = 19 4+5+10=19


5. 关键点解释

边排序

  • 使用 Collections.sort(edges) 按权值排序边,时间复杂度为 O ( E log ⁡ E ) O(E \log E) O(ElogE),其中 E E E 是边的数量。

并查集

  • findunion 操作通过路径压缩和按秩合并优化,时间复杂度接近常数。

贪心策略

  • 每次选择权值最小的边,并确保不会形成环,保证了结果是最优解。

6. 时间复杂度

  • 边排序: O ( E log ⁡ E ) O(E \log E) O(ElogE)
  • 并查集操作: O ( E ⋅ α ( V ) ) O(E \cdot \alpha(V)) O(Eα(V)),其中 α \alpha α 是反阿克曼函数,近似为常数。
  • 总复杂度: O ( E log ⁡ E ) O(E \log E) O(ElogE)

7. 应用场景

  1. 网络设计:
    • 例如设计最小代价的通信网络、电网等。
  2. 图像处理:
    • 用于聚类算法(如分割图像)。
  3. 路径优化:
    • 例如寻找最小代价的运输路径。

总结

  • Kruskal算法是构建最小生成树的有效方法,利用贪心思想和并查集优化。
  • 在稀疏图(边少的图)中表现非常优秀。
  • 理解并查集操作和边排序是掌握Kruskal算法的关键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2245544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智慧路面管理系统平台 智慧照明 智慧市政 智慧交通

智慧路面管理系统平台   智慧路面管理系统平台&#xff0c;旨在提高城市道路的智能化水平和交通效率。该系统通过集成传感器、摄像头、监控设备、大数据、云计算等多种技术手段&#xff0c;实现对道路状况和交通流量的实时监测与分析&#xff0c;从而提供精准的交通数据和智能…

数据结构 ——— 判断一棵树是否是完全二叉树

目录 满二叉树和完全二叉树示意图 手搓一个完全二叉树 代码实现 满二叉树和完全二叉树示意图 注意区分满二叉树和完全二叉树 满二叉树的每一层都是满的&#xff0c;也就是除了叶子节点&#xff0c;其他节点都有左右节点 完全二叉树的最后一层不一定是满的&#xff0c;但是从…

Vue_Router权限控制:不同角色显示不同路由

写在前面 在Vue中&#xff0c;Router是一个官方提供的用于处理应用程序路由的插件。它允许我们创建单页应用程序&#xff08;SPA&#xff09;&#xff0c;其中不同的页面和组件可以通过URL进行导航和展示。使我们可以轻松地创SPA&#xff0c;并实现可复用和可组合的组件…

java多线程并发执行方法或者调用接口

在开发过程中有时需要检查某个接口或者某个方法是否存在并发安全问题&#xff0c;我们会用到jmeter 、AB 等压测工具辅助我们完成代码测试&#xff0c;虽然这些工具功能很强大&#xff0c;也很好用&#xff0c;但是在开发过程中来使用还是不如直接执行Test 或者main 方法来的方…

Python小游戏28——水果忍者

首先&#xff0c;你需要安装Pygame库。如果你还没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; 【bash】 pip install pygame 《水果忍者》游戏代码&#xff1a; 【python】 import pygame import random import sys # 初始化Pygame pygame.init() # 设置屏幕尺寸 …

测评部署和管理 WordPress 最方便的面板

新版宝塔面板快速搭建WordPress新手教程 - 倚栏听风-Morii - 博客园 初学者使用1Panel面板快速搭建WordPress网站 - 倚栏听风-Morii - 博客园 可以看到&#xff0c;无论是宝塔还是1Panel&#xff0c;部署和管理WordPress都有些繁琐&#xff0c;而且还需要额外去配置Nginx和M…

OpenAI Adjusts Strategy as ‘GPT’ AI Progress Slow

注&#xff1a;本文为两篇关于当前大模型方向讨论的文章。 OpenAI 大改下代大模型方向&#xff0c;scaling law 撞墙&#xff1f;AI 社区炸锅了 机器之心 2024 年 11 月 11 日 11:57 北京 机器之心报道 编辑&#xff1a;Panda、泽南 大模型的 scaling law 到头了&#xff1f…

Java开发者必备:23种设计模式全面解析

文章目录 一、创建型模式1、工厂模式简单工厂工厂方法 2、抽象工厂模式3、原型模式4、建造者模式5、单例模式 二、结构型模式1、适配器模式2、桥接模式3、组合模式4、装饰模式5、外观模式6、享元模式7、代理模式 三、行为型模式1、解释器模式2、模板方法模式3、策略模式4、观察…

LeetCode:1008. 前序遍历构造二叉搜索树

目录 题目描述: 代码: 第一种: 第二种: 第三种:分治法 题目描述: 给定一个整数数组&#xff0c;它表示BST(即 二叉搜索树 )的 先序遍历 &#xff0c;构造树并返回其根。 保证 对于给定的测试用例&#xff0c;总是有可能找到具有给定需求的二叉搜索树。 二叉搜索树 是一棵…

STM32F103 GPIO和串口实战

本节我们将会对STM32F103的硬件资源GPIO和串口进行介绍。 一、GPIO 1.1 电路原理图 LED电路原理图如下图所示&#xff1a; 其中&#xff1a; LED1连接到PA8引脚&#xff0c;低电平点亮&#xff1b;LED2连接到PD2引脚&#xff0c;低电平点亮&#xff1b; 1.2 GPIO引脚介绍 STM32…

Statsmodels之OLS回归

目录 Statsmodels基本介绍OLS 回归实战实战1&#xff1a;实战2&#xff1a; Statsmodels基本介绍 Statsmodels 是 Python 中一个强大的统计分析包&#xff0c;包含了回归分析、时间序列分析、假设检验等等的功能。Statsmodels 在计量的简便性上是远远不及 Stata 等软件的&…

在 macOS 和 Linux 中,波浪号 `~`的区别

文章目录 1、在 macOS 和 Linux 中&#xff0c;波浪号 ~macOS示例 Linux示例 区别总结其他注意事项示例macOSLinux 结论 2、root 用户的主目录通常是 /root解释示例切换用户使用 su 命令使用 sudo 命令 验证当前用户总结 1、在 macOS 和 Linux 中&#xff0c;波浪号 ~ 在 macO…

人工智能之机器学习5-回归算法2【培训机构学习笔记】

培训班ppt内容&#xff1a; 个人精进总结&#xff1a; 可解释方差 定义 可解释方差的回归评分函数是一种用于评估回归模型性能的指标&#xff0c;以下从其定义、计算公式、取值范围及意义、应用场景等方面进行详细介绍&#xff1a; 可解释方差&#xff08;Explained Varian…

vue2中引入cesium全步骤

1.npm 下载cesium建议指定版本下载&#xff0c;最新版本有兼容性问题 npm install cesium1.95.0 2.在node_models中找到cesium将此文件下的Cesium文件复制出来放在项目的静态资源public中或者static中&#xff0c;获取去github上去下载zip包放在本地也可以 3.在index.html中引…

数据结构(顺序栈——c语言实现)

栈的基本概念&#xff1a; 栈是限制在一端进行插入操作和删除操作的线性表&#xff08;俗称堆栈&#xff09;&#xff0c;允许进行操作的一端称为“栈顶”&#xff0c;另一固定端称为“栈底”&#xff0c;当栈中没有元素时称为“空栈” 特点&#xff1a;先进后出&#xff08;FI…

基于Windows系统用C++做一个点名工具

目录 一、前言 二、主要技术点 三、准备工作 四、主界面 1.绘制背景图 2、实现读取花名册功能 3.实现遍历花名册功能 4.实现储存功能 4.1创建数据库 4.2存储数据到数据库表 4.3读取数据库表数据 一、前言 人总是喜欢回忆过去&#xff0c;突然回忆起…

前端监控之sourcemap精准定位和还原错误源码

一、概述 在前端开发中&#xff0c;监控和错误追踪是确保应用稳定性和用户体验的重要环节。 随着前端应用的复杂性增加&#xff0c;JavaScript错误监控变得尤为重要。在生产环境中&#xff0c;为了优化加载速度和性能&#xff0c;前端代码通常会被压缩和混淆。这虽然提升了性…

算法编程题-排序

算法编程题-排序 比较型排序算法冒泡排序选择排序插入排序希尔排序堆排序快速排序归并排序 非比较型排序算法计数排序基数排序 本文将对七中经典比较型排序算法进行介绍&#xff0c;并且给出golang语言的实现&#xff0c;还包括基数排序、计数排序等非比较型的算法的介绍和实现…

Jenkins修改LOGO

重启看的LOGO和登录页面左上角的LOGO 进入LOGO存在的目录 [roottest-server01 svgs]# pwd /opt/jenkins_data/war/images/svgs [roottest-server01 svgs]# ll logo.svg -rw-r--r-- 1 jenkins jenkins 29819 Oct 21 10:58 logo.svg #jenkins_data目录是我挂载到了/opt目录&…

【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段

文章目录 一、MyBatis-Plus简介二、快速入门1、环境准备2、将mybatis项目改造成mybatis-plus项目&#xff08;1&#xff09;引入MybatisPlus依赖&#xff0c;代替MyBatis依赖&#xff08;2&#xff09;配置Mapper包扫描路径&#xff08;3&#xff09;定义Mapper接口并继承BaseM…