【数据结构与算法】克鲁斯卡尔算法

news2024/11/19 3:36:44

克鲁斯卡尔算法

介绍

  1. 克鲁斯卡尔(Kruskal)算法是用来求加权连通图的最小生成树的算法。
  2. 基本思想:按照权值从小到大的顺序选择 n - 1 条边,并保证这 n - 1 条边不构成回路。
  3. 具体做法:首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止。

克鲁斯卡尔最佳实践 - 公交站问题

在这里插入图片描述

  1. 有北京有新增 7 个站点(A,B,C,D,E,F,G),现在需要修路把 7 个站点连通
  2. 各个站点的距离用边线表示(权),比如 A - B距离 12 公里
  3. 问:如何修路保证各个站点都能连通,并且总的修路总里程最短?

克鲁斯卡尔算法分析

问题一:对图的所有边按照权值大小进行排序

问题二:将边添加到最小生成树中时,怎样判断是否形成回路

问题一很好解决,采用排序算法进行排序即可

问题二,处理方式是:记录顶点在“最小生成树”中的终点,顶点的终点是“在最小生成树中与它连通的最大顶点”。然后每次需要将一条边添加到最小生成树时,判断该边的两个顶点的终点是否重合,重合的话则会构成回路。

代码实现

public class KruskalCase {
    private int edgeNum; // 边的个数
    private char[] vertexs; // 顶点数组
    private int[][] matrix; // 邻接矩阵
    // 使用 INF 表示两个顶点不能连通
    private static final int INF = Integer.MAX_VALUE;

    public static void main(String[] args) {
        char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int matrix[][] = {
                {0, 12, INF, INF, INF, 16, 14},
                {12, 0, 10, INF, INF, 7, INF},
                {INF, 10, 0, 3, 5, 6, INF},
                {INF, INF, 3, 0, 4, INF, INF},
                {INF, INF, 5, 4, 0, 2, 8},
                {16, 7, 6, INF, 2, 0, 9},
                {14, INF, INF, INF, 8, 9, 0}
        };
        // 创建对象实例
        KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);
        kruskalCase.print();
        // 克鲁斯卡尔算法
        kruskalCase.Kruskal();
    }

    /**
     * 构造器初始化
     *
     * @param vertexs 顶点数组
     * @param matrix  邻接矩阵
     */
    public KruskalCase(char[] vertexs, int[][] matrix) {
        // 初始化顶点数和边的个数
        int vlen = vertexs.length;

        // 初始化顶点
        this.vertexs = new char[vlen];
        for (int i = 0; i < vertexs.length; i++) {
            this.vertexs[i] = vertexs[i];
        }

        // 初始化边
        this.matrix = new int[vlen][vlen];
        for (int i = 0; i < vlen; i++) {
            for (int j = 0; j < vlen; j++) {
                this.matrix[i][j] = matrix[i][j];
            }
        }

        // 统计边
        for (int i = 0; i < vlen; i++) {
            for (int j = i + 1; j < vlen; j++) {
                if (this.matrix[i][j] != INF) {
                    edgeNum++;
                }
            }
        }
    }

    /**
     * 克鲁斯卡尔算法
     */
    public void Kruskal() {
        int index = 0; // 表示最后结果数组的索引
        int[] ends = new int[edgeNum]; // 用于保存“已有最小生成树”中的每个顶点在最小生成树中的终点
        // 创建结果数组,保存最后的最小生成树
        EData[] rets = new EData[edgeNum];
        // 获取图中所有的边的集合
        EData[] edges = getEdges();
        // 按照边的权值大小进行排序
        sortEdges(edges);
        System.out.println("图的边的集合=" + Arrays.toString(edges) + " 共" + edges.length);
        // 遍历 edges,将边加入到最小生成树中时,判断准备加入的边是否形成了回路,如果没有,就加入
        for (int i = 0; i < edgeNum; i++) {
            // 获取到第 i 条边的第 1 个顶点
            int p1 = getPosition(edges[i].start);
            // 获取到第 i 条边的第 2 个顶点
            int p2 = getPosition(edges[i].end);
            // 获取 p1 顶点在已有的最小生成树中的终点
            int m = getEnd(ends, p1);
            // 获取 p2 顶点在已有的最小生成树中的终点
            int n = getEnd(ends, p2);
            // 判断是否构成回路
            if (m != n) {
                ends[m] = n; // 设置 m 在“已有最小生成树”中的终点
                rets[index++] = edges[i]; // 有一条边加入到 rets 数组
            }
        }

        // 统计并打印“最小生成树”,输出 rets
        System.out.println("最小生成树为:");
        for (int i = 0; i < index; i++) {
            System.out.println(rets[i]);
        }
    }

    /**
     * 打印
     */
    public void print() {
        System.out.println("邻接矩阵为:");
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = 0; j < vertexs.length; j++) {
                System.out.printf("%10d\t", matrix[i][j]);
            }
            System.out.println();
        }
    }

    /**
     * 对边进行排序处理,冒泡
     *
     * @param edges 边的集合
     */
    private void sortEdges(EData[] edges) {
        for (int i = 0; i < edges.length - 1; i++) {
            for (int j = 0; j < edges.length - 1 - i; j++) {
                if (edges[j].weight > edges[j + 1].weight) {
                    EData tmp = edges[j];
                    edges[j] = edges[j + 1];
                    edges[j + 1] = tmp;
                }
            }
        }
    }

    /**
     * 得到顶点对应下标
     *
     * @param ch 顶点的值
     * @return 返回顶点的下标,找不到返回 -1
     */
    private int getPosition(char ch) {
        for (int i = 0; i < vertexs.length; i++) {
            if (vertexs[i] == ch) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取图中的边,放到 EData[] 数组中
     * 通过 matrix 邻接矩阵获取
     *
     * @return
     */
    private EData[] getEdges() {
        int index = 0;
        EData[] edges = new EData[edgeNum];
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = i + 1; j < vertexs.length; j++) {
                if (matrix[i][j] != INF) {
                    edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);
                }
            }
        }
        return edges;
    }

    /**
     * 获取下标为 i 的顶点的终点,用于后面判断两个顶点的终点是否相同
     *
     * @param ends 记录各个顶点对应的终点是哪个,ends 是在遍历过程中逐步形成的
     * @param i    表示传入的顶点对应的下标
     * @return 返回下标为 i 的这个顶点对应的终点的下标
     */
    private int getEnd(int[] ends, int i) {
        while (ends[i] != 0) {
            i = ends[i];
        }
        return i;
    }
}

// 创建 EData,他的对象实例表示边
class EData {
    char start; // 边的起点
    char end; // 边的终点
    int weight; // 边的权值

    public EData(char start, char end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "EData[" +
                "<" + start +
                ", " + end +
                "> = " + weight +
                ']';
    }
}

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

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

相关文章

文本三剑客sed grep awk

目录 1、sed 1.1、基本用法 1.2、sed脚本格式 1.3、搜索与替换 1.4、变量 2、awk 2.1、基础用法 2.2、常见的内置变量 2.3、模式 2.4、判断 2.5、for计算 2.6、数组 3、grep 1、sed sed 即 Stream EDitor&#xff0c;和 vi 不同&#xff0c;sed是行编辑器 Sed是从…

leetcode刷题之283:移动零

问题 实现思路 首先, 将dest指向-1 位置, cur指向下标为0 的位置, 在cur遍历的过程中: 1) 遇到非零元素则与下标dest1 位置的元素交换, 2) 若遇到零元素则只继续cur遍历. 下标为1 的位置上是 非零元素 执行1) 交换得到右图结果 随后cur 得到下图结果 下标为2 的位置上是零…

day-27 代码随想录算法训练营(19)part03

78.子集 画图分析&#xff1a; 思路&#xff1a;横向遍历&#xff0c;每次遍历的时候都进行一次添加&#xff0c;然后进行纵向递归&#xff0c;递归完之后进行回溯。 注意&#xff1a;空集也是子集。 90.子集|| 分析&#xff1a;和上题一样&#xff0c;区别在于有重复数字 …

LeetCode283.移动零

这道题还是很简单的&#xff0c;我用的是双指针&#xff0c;左指针i从头开始遍历数组&#xff0c;右指针j是从i后面第一个数开始遍历&#xff0c;当左指针i等于0的时候&#xff0c;右指针j去寻找i右边第一个为0的数和i交换位置&#xff0c;交换完了就break内层循环&#xff0c;…

STM8遇坑[EEPROM读取debug不正常release正常][ STVP下载成功单运行不成功][定时器消抖莫名其妙的跑不通流程]

EEPROM读取debug不正常release正常 这个超级无语,研究和半天,突然发现调到release就正常了,表现为写入看起来正常读取不正常,这个无语了,不想研究了 STVP下载不能够成功运行 本文摘录于&#xff1a;https://blog.csdn.net/qlexcel/article/details/71270780只是做学习备份之…

每周AI大事件 百度文心一言上线搜索、文生视频、图表制作等5大插件

每周AI大事件 | 百度文心一言上线搜索、文生视频、图表制作等5大插件 文章目录 一、百度文心一言简介二、百度文心一言五大插件功能详解三、 开启文心一言 体验览卷文档E言易图 &#xff08;貌似不太理想&#xff0c;可能指令姿势不对&#xff09;说图解画&#xff08;貌似不太…

「第2讲」正版PyCharm但是免费,安装教程来了,还有中文插件哦~

大家好&#xff0c;这里是程序员晚枫。 免费的【50讲Python自动化办公】持续更新中&#xff0c;关注我学习吧&#x1f447;想了解更多精彩内容&#xff0c;快来关注程序员晚枫 上一讲&#xff1a;「第1讲」Python的下载、安装和卸载&#xff0c;有手就能学 装完了Python&#…

char *str,char str,char * str和char str的区别

1.char *str是一个指向字符或字符串的指针&#xff0c;总是指向一个字符的起始地址&#xff0c;例如 char *str "Hello"; cout << *str << endl; // 输出&#xff1a;H cout << str << endl; // 输出&#xff1a;Hello str "World…

5.4 webrtc的线程

那今天呢&#xff1f;我们来了解一下webrtc中的threed&#xff0c;首先我们看一下threed的类&#xff0c;它里边儿都含了哪些内容&#xff1f;由于threed的类非常大啊&#xff0c;我们将它分成两部分。 那第一部分呢&#xff0c;是我们看threed的类中都包含了哪些数据之后呢&a…

linux设备驱动:kset、uevent、class

目录 kset&#xff1a;驱动的骨架 kset_create_and_add()函数 设备驱动模型实验2-kobject点灯&#xff08;加入kset&#xff09; kset.c文件 Makefile文件 执行过程 uevent&#xff1a;内核消息的快递包 uevent机制 kobject_uevent()函数 设备驱动模型实验3-kobject点…

AMBA总线协议(3)——AHB(一)

目录 一、前言 二、什么是AHB总线 1、概述 2、一个典型的基于AHB总线的微处理器架构 3、基本的 AHB 传送特性 三、AMBA AHB总线互联 四、小结 一、前言 在之前的文章中我们初步的了解了一下AMBA总线中AHB,APB,AXI的信号线及其功能&#xff0c;从本文开始我们…

NOIP2014普及组复赛 珠心算测验 螺旋矩阵 真题答案

珠心算测验 说明 珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练&#xff0c; 既能够开发智力&#xff0c;又能够为日常生活带来很多便利&#xff0c;因而在很多学校得到普及。 某学校的珠心算老师采用一种快速考察珠心算加法能力的测验方法。他…

wustoj2006后天

#include <stdio.h> int main() {int n;scanf("%d",&n); printf("%d",(n2)%7);return 0;}

星际争霸之小霸王之小蜜蜂(一)--窗口界面设计

目录 前言 一、安装pygame库 1、pygame库简介 2、在windows系统安装pygame库 二 、搭建游戏框架 1、创建游戏窗口 2、改变窗口颜色 总结 前言 大家应该都看过或者都听说过python神书“大蟒蛇”&#xff0c;上面有一个案例是《外星人入侵》&#xff0c;游戏介绍让我想起了上…

上位机系统(系统的架构、串口的使用、协议的定义、开发环境的配置)

上位机系统 1. 系统架构 实机拓扑架构 硬件支持 使用 VSPD 6.9 实现&#xff1a; 效果图 当状态值超过警戒值&#xff0c;就会变成红色&#xff0c;同时在界面的上方显示红色的“设备告警” 3. 串口电气特性 波特率&#xff1a;19200 数据位数&#xff1a;8 位 u 奇偶校验&…

shell脚本之函数

shell函数 函数的组成&#xff1a;函数名和函数体 函数的格式 function 函数名 { 命令序列 } function cat {cat /etc/passwd}函数名() { 命令序列 } cat () {cat /etc/passwd}function 函数名 (){ 命令序列 } function cat() {cat /etc/passwd}函数相关命令 declare -F #查…

记录每日LeetCode 2236. 判断根结点是否等于子结点之和 Java实现

题目描述&#xff1a; 给你一个 二叉树 的根结点 root&#xff0c;该二叉树由恰好 3 个结点组成&#xff1a;根结点、左子结点和右子结点。 如果根结点值等于两个子结点值之和&#xff0c;返回 true &#xff0c;否则返回 false 。 初始代码&#xff1a; /*** Definition f…

Cpp学习——类与对象3

目录 一&#xff0c;初始化列表 1.初始化列表的使用 2.初始化列表的特点 3.必须要使用初始化列表的场景 二&#xff0c;单参数构造函数的隐式类型转换 1.内置类型的隐式类型转换 2. 自定义类型的隐式类型转换 3.多参数构造函数的隐式类型转换 4.当你不想要发生隐式类型转换…

7-7 找最小的字符串

分数 15 全屏浏览题目 切换布局 作者 张泳 单位 浙大城市学院 本题要求编写程序&#xff0c;针对输入的N个字符串&#xff0c;输出其中最小的字符串。 输入格式&#xff1a; 输入第一行给出正整数N&#xff1b;随后N行&#xff0c;每行给出一个长度小于80的非空字符串&…