Java求最小生成树的两种算法详解

Java求最小生成树的两种算法详解

概述

最小生成树(Minimum Spanning Tree)是指在一张连通的、有权图中找到一棵权值和最小的生成树,它是一些算法的子问题,常用于解决带权无向图的问题。常见的最小生成树算法有Prim算法和Kruskal算法,本文将详细讲解这两种算法的实现原理及其Java代码实现。

Prim算法

Prim算法是一种贪心算法,通过每次选择当前权值最小的边来构建最小生成树。具体实现过程如下:

  1. 从任意一个节点出发,将其加入生成树中。
  2. 找到与树相邻的最小权值的边,并将其连接到树上。
  3. 将该边连接的节点加入生成树中。
  4. 重复步骤2和步骤3,直到所有的节点都被加入生成树中。

下面是Prim算法的Java实现,其中graph是一个存储图信息的二维数组,n是图中节点的个数:

int prim(int[][] graph, int n) {
    int[] dis = new int[n];  // 存储每个节点到已选择节点的最短距离
    boolean[] vis = new boolean[n];  // 存储每个节点是否已经被选择
    Arrays.fill(dis, Integer.MAX_VALUE);
    dis[0] = 0;
    int ans = 0;
    for (int i = 0; i < n; i++) {
        int u = -1;
        for (int j = 0; j < n; j++) {  // 找到未被选择节点中距离已选择节点最近的节点
            if (!vis[j] && (u == -1 || dis[j] < dis[u])) {
                u = j;
            }
        }
        vis[u] = true;
        ans += dis[u];  
        for (int v = 0; v < n; v++) {  // 更新未被选择节点的最短距离
            if (!vis[v]) {
                dis[v] = Math.min(dis[v], graph[u][v]);
            }
        }
    }
    return ans;
}

示例1:给定以下五个节点之间的连通情况和边权值,求最小生成树的权值和。

   2 - 3 - 4
 /   |   / |
1    4 5   3
 \  /   \ |
   0 - 6 - 7

其中graph矩阵如下:

int[][] graph = {
        {0, 2, Integer.MAX_VALUE, Integer.MAX_VALUE, 1, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
        {2, 0, 4, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
        {Integer.MAX_VALUE, 4, 0, 5, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 3},
        {Integer.MAX_VALUE, Integer.MAX_VALUE, 5, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 4},
        {1, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
        {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, 6, Integer.MAX_VALUE},
        {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 6, 0, 7},
        {Integer.MAX_VALUE, Integer.MAX_VALUE, 3, 4, Integer.MAX_VALUE, Integer.MAX_VALUE, 7, 0}
};
int ans = prim(graph, 8);
System.out.println(ans);

输出:10,表示最小生成树的权值和为10。

Kruskal算法

Kruskal算法是一种基于贪心的算法,通过不断选择图中权值最小的边来构建最小生成树。具体实现过程如下:

  1. 将所有的边按权值从小到大排序。
  2. 依次选择每条边,如果该边不会与已经选择的边构成环,则将其加入生成树中,否则忽略该边。
  3. 重复步骤2,直到生成树的边数为n-1。

下面是Kruskal算法的Java实现,其中graph是一个存储图信息的二维数组,n是图中节点的个数:

class Edge implements Comparable<Edge> {
    int from, to, w;
    Edge(int f, int t, int w) {
        this.from = f;
        this.to = t;
        this.w = w;
    }
    @Override
    public int compareTo(Edge o) {  // 重写比较函数
        return w - o.w;  // 按边的权值从小到大排序
    }
}
int kruskal(int[][] graph, int n) {
    Edge[] edges = new Edge[n * (n - 1) / 2];  // 存储所有的边
    int idx = 0;
    for (int i = 0; i < n; i++) {
        for (int j = i+1; j < n; j++) {
            if (graph[i][j] != Integer.MAX_VALUE) {
                edges[idx++] = new Edge(i, j, graph[i][j]);
            }
        }
    }
    Arrays.sort(edges);  // 按边的权值从小到大排序
    int ans = 0;
    int[] uf = new int[n];
    for (int i = 0; i < n; i++) {
        uf[i] = i;
    }
    for (Edge e : edges) {
        int fu = find(e.from, uf);
        int fv = find(e.to, uf);
        if (fu == fv) {  // 若加入该边会形成环,则忽略该边
            continue;
        }
        uf[fu] = fv;
        ans += e.w;
    }
    return ans;
}
int find(int x, int[] uf) {
    if (x != uf[x]) {
        uf[x] = find(uf[x], uf);  // 路径压缩
    }
    return uf[x];
}

示例2:和示例1中的图相同,求最小生成树的权值和,但采用Kruskal算法。

int[][] graph = {
        {0, 2, Integer.MAX_VALUE, Integer.MAX_VALUE, 1, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
        {2, 0, 4, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
        {Integer.MAX_VALUE, 4, 0, 5, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 3},
        {Integer.MAX_VALUE, Integer.MAX_VALUE, 5, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 4},
        {1, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
        {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, 6, Integer.MAX_VALUE},
        {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 6, 0, 7},
        {Integer.MAX_VALUE, Integer.MAX_VALUE, 3, 4, Integer.MAX_VALUE, Integer.MAX_VALUE, 7, 0}
};
int ans = kruskal(graph, 8);
System.out.println(ans);

输出:10,表示最小生成树的权值和为10。

总结

本篇文章详细讲解了Prim算法和Kruskal算法的实现原理及其Java代码实现,并分别进行了两个示例的说明。这两种算法都可以在处理带权无向图的最小生成树问题上得到良好的应用。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java求最小生成树的两种算法详解 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • 模拟实现strlen的三种方法

    一、strlen()的工作原理 二、模拟实现strlen的三种方法 计数器方法 指针-指针 递归的方法 三、库函数实现strlen的思路 四、库函数的strlen同上面模拟实现strlen的区别 一、strlen工作原理 strlen函数工作原理:是计算字符串str的长度,直到空字符串结束,但不包含空字符串。(即该长度算至/0结束,但不包含/0) 通过以下代…

    C语言 2023年4月18日
    00
  • C语言使用setjmp和longjmp实现一个简单的协程

    下面是C语言使用setjmp和longjmp实现一个简单的协程的完整攻略。 什么是协程 协程是一种并发编程模型,可以看做是一种用户空间的轻量级线程。协程特点是占用资源少,切换代价低,不需要线程上下文切换的开销,仅通过自己写的切换机制进行上下文切换。由于协程不需要访问操作系统资源,因此基本不会发生阻塞的现象,其在I/O密集型任务中具有很好的应用前景。 使用se…

    C 2023年5月24日
    00
  • C语言 位运算详解及示例代码

    C语言 位运算详解及示例代码 什么是位运算 在计算机中,数据存储采用二进制的形式,二进制位只有0和1两个取值。位运算是一种直接针对二进制位进行操作的运算,常见的位运算包括按位与、按位或、按位异或、位左移、位右移等。 位运算的分类 在C语言中,位运算可以分为3类:按位逻辑运算符、按位位移运算符和按位赋值运算符。 按位逻辑运算符 按位逻辑运算符用于操作二进制数中…

    C 2023年5月30日
    00
  • 关于C语言程序的内存分配的入门知识学习

    关于C语言程序的内存分配的入门知识,要了解到以下内容: 1. 内存的基本概念 计算机是由中央处理器(CPU)、内存和硬盘等电子装置组成的。内存是程序运行时存储数据和代码的临时存储器,程序每次运行都需要占用内存,当程序结束后就会释放相应的内存。 2. 栈与堆的比较 在程序中,常见的内存分配方式有栈和堆两种,它们都是存储数据的区域,但其具体的使用方式有所不同。-…

    C 2023年5月23日
    00
  • C语言自定义函数的实现

    C语言中自定义函数的实现可以分为以下几个步骤: 函数声明 : 在使用函数之前,需要先声明函数。函数声明分为两种,一种是函数原型声明,另一种是直接写函数定义。 函数定义:函数定义包括函数名、入参、返回值和函数体。其中函数体是自定义函数的核心部分。 函数调用:调用自定义函数需要使用函数名,并传递相应的参数,等待函数返回相应的结果。 下面,我们用两个示例来说明自定…

    C 2023年5月23日
    00
  • C指针原理教程之C快速入门

    “C指针原理教程之C快速入门”是一篇讲解C语言指针的指南,它详细地介绍了C指针的概念、基础与进阶知识,适用于所有初学者或需要加深自己基础知识的人。下面将为你详细讲解这篇攻略。 C指针原理教程之C快速入门:介绍指针 本节主要介绍指针的概念和基础知识,包括定义指针、指针的运算等。同时,本节也将介绍指针的应用,例如数组、函数调用等。 C指针原理教程之C快速入门:指…

    C 2023年5月22日
    00
  • Win10系统共享打印机0x000003ec连接失败怎么办?(附解决方法)

    Win10系统共享打印机0x000003ec连接失败怎么办?(附解决方法) 问题描述 在 Win10 系统中,尝试连接共享打印机时,可能会遇到错误提示 0x000003ec,即“Windows 无法安装该打印机”。此时需要解决该问题,才能成功连接共享打印机。 解决方法 方法一:重置打印机池服务 按下快捷键 Win + R 打开运行窗口; 输入 service…

    C 2023年5月23日
    00
  • js字符串转成JSON

    假设我们有一个字符串 str,它代表一个 JSON 对象,现在需要把它转成 JavaScript 对象,下面是实现的完整攻略。 1. 将字符串解析成 JSON 对象 使用 JSON.parse() 函数可以将字符串转为 JSON 对象,该函数有一个参数,即要解析的 JSON 字符串。 下面是一个示例: const jsonStr = ‘{"name…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部