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日

相关文章

  • C#使用Clipboard类实现剪贴板功能

    C#使用Clipboard类实现剪贴板功能 导读 剪贴板是电脑生产过程中至关重要的一部分,通过使用剪贴板,我们可以在不同的应用程序和文本之间快速、方便地复制和粘贴数据。在C#中,我们可以使用Clipboard类来实现剪贴板的功能,本文将详细讲解如何在C#应用程序中使用Clipboard类来实现剪贴板功能。 使用Clipboard类 在C#中,Clipboar…

    C 2023年5月23日
    00
  • C++头文件和cpp文件的原理分析

    下面我会为你详细讲解“C++头文件和cpp文件的原理分析”的完整攻略,包含以下内容: C++头文件和cpp文件的作用: 头文件和cpp文件相当于C++中的两个重要的分离式编译的机制。「头文件」通常包含程序所用到的函数的声明和类的定义,而「cpp文件」则包含函数的实现和类的方法定义。C++通过将程序分割为不同的文件来提高软件的可维护性和可扩展性,使得每个文件包…

    C 2023年5月23日
    00
  • Java实现生成JSON字符串的三种方式分享

    以下是 “Java实现生成JSON字符串的三种方式分享” 的完整攻略: 一、使用Java的JSONObject实现 在Java中,可以使用JSONObject类来生成JSON字符串,该类定义了用于创建和操作JSON对象的方法。下面是一个示例: import org.json.*; public class JSONDemo { public static v…

    C 2023年5月23日
    00
  • C语言中如何进行代码优化?

    代码优化是提高程序性能和运行效率的必要手段,也是编程中一个重要的环节。C语言中进行代码优化可以采取如下措施: 1. 优化算法 在编程中,算法的选择对程序性能影响较大,常见的提高算法效率的方法有: 1.1 使用空间换时间的算法 如果内存空间充足的情况下,可以采用空间复杂度高但时间复杂度低的算法,避免使用时间复杂度高但空间复杂度低的算法,从而提高程序性能。 例如…

    C 2023年4月27日
    00
  • C++小知识:用合适的工具来分析你的代码

    C++小知识:用合适的工具来分析你的代码的攻略如下: 步骤一:选择分析工具 要分析和优化C++代码,我们需要选择一款专门的分析工具。这里推荐几个常用的工具: Valgrind:一款用于检查内存错误的工具 GProf:一款用于分析程序性能瓶颈的工具 Clang Static Analyzer:一款用于静态代码分析的工具 步骤二:对代码进行分析 选择了合适的工具…

    C 2023年5月30日
    00
  • C程序 通过创建一个函数来检查素数

    创建一个函数来检查素数是一个常见的C语言编程问题。下面是一个步骤指南和示例示范。 步骤指南 步骤如下: 定义函数的名称和返回类型。由于函数检查一个数字是否为素数,因此我们可以定义函数为 isPrime(),且函数返回类型为 int,因为我们需要返回0或1。 在函数内部定义一个整数 i 用于循环。我们需要从2到输入数字的平方根进行循环,判断输入数字是否能被整除…

    C 2023年5月9日
    00
  • C语言如何使用函数求素数和举例

    此处我将为您详细讲解关于C语言如何使用函数求素数的完整攻略。整个流程大致分为以下几步: 步骤一:编写函数判断素数 首先,我们需要编写一个函数来判断一个数是否是素数。可以将这个函数定义为:bool isPrime(int n),其中n是待判断的整数,返回值为布尔类型,表示n是否是素数。这个函数的实现过程如下: bool isPrime(int n) { if …

    C 2023年5月23日
    00
  • Linux线程管理必备:解析互斥量与条件变量的详解

    让我来详细讲解一下 “Linux线程管理必备:解析互斥量与条件变量的详解”的完整攻略。 简介 在Linux下进行线程管理使用互斥量和条件变量是非常常见的。互斥量提供了对访问共享资源的互斥访问,条件变量允许一个线程等待特定条件的出现。本攻略将简要介绍互斥量和条件变量的概念、实现方式及相关应用,以及在Linux下使用互斥量和条件变量的示例代码。 互斥量介绍 互斥…

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