最小生成树Prim + Kruskal

news2024/11/17 7:41:17

最小生成树

    • 是否成环
      • 并查集只能判断无向图是否成环
    • Kruskal
    • Prim
      • 普通写法
      • 优先级队列的写法

最小生成树仅仅针对无向图

是否成环

参考链接
这里直接用carl给的模板

int n = 1005; // n根据题目中节点数量而定,一般比节点数量大一点就好
vector<int> father = vector<int> (n, 0); // C++里的一种数组结构

// 并查集初始化
void init() {
    for (int i = 0; i < n; ++i) {
        father[i] = i;
    }
}
// 并查集里寻根的过程
int find(int u) {
    return u == father[u] ? u : father[u] = find(father[u]); // 路径压缩
}

// 判断 u 和 v是否找到同一个根
bool isSame(int u, int v) {
    u = find(u);
    v = find(v);
    return u == v;
}

// 将v->u 这条边加入并查集
void join(int u, int v) {
    u = find(u); // 寻找u的根
    v = find(v); // 寻找v的根
    if (u == v) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回
    father[v] = u;
}

通过模板,我们可以知道,并查集主要有三个功能。

  1. 寻找根节点,函数:find(int u),也就是判断这个节点的祖先节点是哪个
  2. 将两个节点接入到同一个集合,函数:join(int u, int v),将两个节点连在同一个根节点上
  3. 判断两个节点是否在同一个集合,函数:isSame(int u, int v),就是判断两个节点是不是同一个根节点

一个误区

// 判断 u 和 v是否找到同一个根
bool isSame(int u, int v) {
    u = find(u);
    v = find(v);
    return u == v;
}

// 将v->u 这条边加入并查集
void join(int u, int v) {
    if (isSame) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回
    father[v] = u;
}

这里不能直接将isSame抽象出来,在join里面调用,必须寻根以后再father[v] = u;

寻根以后的u v 和 join传入的参数u v 是不一样的!!!

并查集只能判断无向图是否成环

public class test4 {

    public static int n;
    public static int[] father;

    public void init() {
        father = new int[n];
        for (int i = 0; i < n; i++) {
            father[i] = i;
        }
    }

    public int find(int u) {
        if (u == father[u]) {
            return u;
        } else
            return father[u] = find(father[u]);
    }

    public boolean isSame(int u, int v) {
        u = find(u);
        v = find(v);
        return u == v;
    }

    public void join(int u, int v) {
        u = find(u);
        v = find(v);
        if (u == v)
            return;
        father[v] = u;
    }

    public static void main(String[] args) {
        test4 t = new test4();
        test4.n = 4;
        t.init();

        t.join(1, 2);
        t.join(2, 3);
        t.join(1, 3);

        System.out.println(Arrays.toString(father));
        System.out.println(t.isSame(1, 3));
    }
}

通过这个例子可以看到,t.join(1, 3);还是t.join(3, 1); 最后判断t.isSame(1, 3)都会是true

在这里插入图片描述

最小生成树参考视频

Kruskal

Kruskal.java

import java.util.*;

class Edge implements Comparable<Edge> {
    int from, to, weight;

    public Edge(int from, int to, int weight) {
        this.from = from;
        this.to = to;
        this.weight = weight;
    }

    @Override
    public int compareTo(Edge other) {
        return this.weight - other.weight;
    }
}

public class Kruskal {
    public static int find(int[] parent, int u) {
        if (u == parent[u]) {
            return u;
        }else{
            return parent[u] = find(parent, parent[u]);
        }
    }

    public static void union(int[] parent, int u, int v) {
        u = find(parent, u);
        v = find(parent, v);
        if(u==v) return;
        parent[u] = v;
    }

    public static List<Edge> kruskalMST(List<Edge> edges, int n) {
        Collections.sort(edges);

        int[] parent = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
		// 最小生成树的边
        List<Edge> result = new ArrayList<>();
        for (Edge edge : edges) {
            int u = find(parent, edge.from);
            int v = find(parent, edge.to);
            // 成环了就跳过
            if (u != v) {
                result.add(edge);
                union(parent, u, v);
            }
        }

        return result;
    }

    public static void main(String[] args) {
        // n 代表顶点的个数
        int n = 4;
        List<Edge> edges = new ArrayList<>();
        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));

        List<Edge> mst = kruskalMST(edges, n);

        for (Edge edge : mst) {
            System.out.println(edge.from + " - " + edge.to + ": " + edge.weight);
        }
    }
}

结果

在这里插入图片描述

Prim

普通写法

Prim.java

public class Prim {
    public static void main(String[] args) {
        // int[][] graph = {
        // {0, 2, 0, 6, 0},
        // {2, 0, 3, 8, 5},
        // {0, 3, 0, 0, 7},
        // {6, 8, 0, 0, 9},
        // {0, 5, 7, 9, 0}
        // };
        int[][] graph = {
                { 0, 4, 0, 0, 0, 0, 0, 8, 0 },
                { 4, 0, 8, 0, 0, 0, 0, 11, 0 },
                { 0, 8, 0, 7, 0, 4, 0, 0, 2 },
                { 0, 0, 7, 0, 9, 14, 0, 0, 0 },
                { 0, 0, 0, 9, 0, 10, 0, 0, 0 },
                { 0, 0, 4, 14, 10, 0, 2, 0, 0 },
                { 0, 0, 0, 0, 0, 2, 0, 1, 6 },
                { 8, 11, 0, 0, 0, 0, 1, 0, 7 },
                { 0, 0, 2, 0, 0, 0, 6, 7, 0 },
        };

        int[] parent = prim(graph);

        // 打印最小生成树的边
        for (int i = 1; i < parent.length; i++) {
            System.out.println("parent["+i+"]="+parent[i] );
        }
    }

    public static int[] prim(int[][] graph) {
        int n = graph.length;
        boolean[] visited = new boolean[n];
        int[] parent = new int[n];
        int[] key = new int[n];

        // 初始化距离数组和父节点数组
        for (int i = 0; i < n; i++) {
            key[i] = Integer.MAX_VALUE;
            parent[i] = -1;
        }
        key[0] = 0;

        // Prim 算法
        for (int i = 0; i < n; i++) {
            int u = -1;
            // 这个for是为了找出当前最近的节点,并标记为已访问
            for (int j = 0; j < n; j++) {
                if (!visited[j] && (u == -1 || key[j] < key[u])) {
                    u = j;
                }
            }

            visited[u] = true;

            // 这个for是为了看那些没有被访问过的节点,更新他们的距离
            for (int v = 0; v < n; v++) {
                if (!visited[v] && graph[u][v] != 0 && graph[u][v] < key[v]) {
                    key[v] = graph[u][v];
                    parent[v] = u;
                }
            }
        }

        return parent;
    }
}

最小生成树的结果不唯一

上述代码的运行结果为

在这里插入图片描述
另外一种树为,权值和均为37
在这里插入图片描述

在这里插入图片描述

优先级队列的写法

Prim2.java

import java.util.PriorityQueue;

public class Prim2 {
    public static void main(String[] args) {
        int[][] graph = {
                { 0, 4, 0, 0, 0, 0, 0, 8, 0 },
                { 4, 0, 8, 0, 0, 0, 0, 11, 0 },
                { 0, 8, 0, 7, 0, 4, 0, 0, 2 },
                { 0, 0, 7, 0, 9, 14, 0, 0, 0 },
                { 0, 0, 0, 9, 0, 10, 0, 0, 0 },
                { 0, 0, 4, 14, 10, 0, 2, 0, 0 },
                { 0, 0, 0, 0, 0, 2, 0, 1, 6 },
                { 8, 11, 0, 0, 0, 0, 1, 0, 7 },
                { 0, 0, 2, 0, 0, 0, 6, 7, 0 },
        };

        int[] parent = prim(graph);

        // 打印最小生成树的边
        for (int i = 1; i < parent.length; i++) {
            System.out.println("parent[" + i + "]=" + parent[i]);
        }
    }

    public static int[] prim(int[][] graph) {
        int n = graph.length;
        boolean[] visited = new boolean[n];
        int[] parent = new int[n];
        int[] key = new int[n];

        // 初始化距离数组和父节点数组
        for (int i = 0; i < n; i++) {
            key[i] = Integer.MAX_VALUE;
            parent[i] = -1;
        }
        key[0] = 0;

        // 使用优先队列存储待处理的顶点
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
        for (int i = 0; i < n; i++) {
            pq.offer(new int[] { key[i], i });
        }

        // Prim 算法
        while (!pq.isEmpty()) {
            int[] minNode = pq.poll();
            int u = minNode[1];
            visited[u] = true;

            for (int v = 0; v < n; v++) {
                if (!visited[v] && graph[u][v] != 0 && graph[u][v] < key[v]) {
                    key[v] = graph[u][v];
                    parent[v] = u;
                    pq.offer(new int[] { key[v], v });
                }
            }
        }

        return parent;
    }
}

优先级队列的思想还是比较容易理解的,刚好其输出是另外一颗最小树,也刚好验证了上面的说法

在这里插入图片描述

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

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

相关文章

win11开启IPV6并手动设置地址

win11开启IPV6并手动设置地址 ipv6手动设置 假设你想要配置的IPv6地址是2001:0db8:85a3:0000:0000:8a2e:0370:7334&#xff0c;子网前缀长度为64位&#xff0c;并且默认网关是2001:0db8:85a3::1。 手动配置IPv6地址的示例步骤&#xff08;Windows操作系统&#xff09;&#x…

【Redis | 第一篇】快速了解Redis

文章目录 1.快速了解Redis1.1简介1.2与其他key-value存储的不同处1.3Redis安装——Windows环境1.3.1下载redis1.3.2启动redis1.3.3进入redis客户端1.3.4修改配置 1.4Redis安装——Linux环境1.4.1安装命令1.4.2启动redis1.4.3进入redis客户端 1.5配置修改1.6小结 1.快速了解Redi…

数据库管理-第156期 Oracle Vector DB AI-07(20240227)

数据库管理156期 2024-02-27 数据库管理-第156期 Oracle Vector DB & AI-07&#xff08;20240227&#xff09;1 Vector相关DDL操作可以在现有的表上新增vector数据类型的字段&#xff1a;可以删除包含vector数据类型的列&#xff1a;可以使用CTAS的方式&#xff0c;从其他有…

【vue+element ui】大屏自适应中el-select下拉内容在低分辨率下显示不全问题解决

目录 背景 现象 解决方案 背景 最近要把一个1920px*1080px的大屏改成自适应的&#xff1b;最低适配到1028px*720px&#xff1b; 现象 自适应适配改完之后 将电脑屏幕改成1028px*720px分辨率后&#xff0c;下拉显示正常 通过谷歌浏览器设置Toggle device toolbar为1028px*…

ArcGIS Runtime For Android开发之符号化和图层渲染

一、用Symbol对要素进行符号化 首先我们看一下Symbol 接口关系&#xff1a; 1、SimpleFillSymbol 他是用来进行简单的Graphic面要素填充符号化的&#xff0c;它可以设置要素的填充颜色&#xff0c;边线颜色、线宽&#xff0c;其用法如下&#xff1a; Polygon polygonnew Po…

并查集例题(食物链)C++(Acwing)

代码&#xff1a; #include <iostream>using namespace std;const int N 50010;int n, m; int p[N], d[N];int find(int x) {if(p[x] ! x){int t find(p[x]);d[x] d[p[x]];p[x] t;}return p[x]; }int main() {scanf("%d%d", &n, &m);for(int i 1…

solidity编程

一.Solidity 简介 Solidity 是⼀种⽤于编写以太坊虚拟机&#xff08; EVM &#xff09;智能合约的 编程语⾔。我认为掌握 Solidity 是参与链上项⽬的必备技 能&#xff1a;区块链项⽬⼤部分是开源的&#xff0c;如果你能读懂代码&#xff0c;就可以 规避很多亏钱项⽬。…

HCIA-Datacom实验指导手册:6 构建基础 WLAN 网络

HCIA-Datacom实验指导手册&#xff1a;6 构建基础 WLAN 网络 一、实验介绍&#xff1a;二、实验拓扑&#xff1a;三、实验目的&#xff1a;四、配置步骤&#xff1a;1.掌握ap上线的配置方式和上线过程。ac配置验证 步骤 2 掌握隧道模式和旁挂模式下ac的配置。步骤 3 掌握查看ap…

【大数据架构(1)】Lambda Architecture – Realtime Data Processing 论文重点翻译

文章目录 1. INTRODUCTION2. LAMBDA ARCHITECTUREA) BATCH LAYERB) SPEED LAYERC) SERVICE LAYER 3. LIMITATIONS OF THE TRADITIONAL LAMBDAARCHITECTURE4. A PROPOSED SOLUTION1. 架构说明2. 前后架构改进对比 1. INTRODUCTION Lambda架构背后的需求是由于虽然MR能够处理大数…

【精选】Java项目介绍和界面搭建——拼图小游戏 上

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

SpringCloudNacos注册中心服务分级存储模型

文章目录 服务分级存储模型概述配置集群同集群优先的负载均衡 权重配置总结 之前对 Nacos注册中心入门 已经做了演示. 这篇文章对 Nacos 的服务分级存储模型做理论与实践. 服务分级存储模型概述 一个服务可以有多个实例&#xff0c;例如我们的 user-server&#xff0c;可以有:…

Codeforces Round 929 (Div. 3)

Codeforces Round 929 (Div. 3) Codeforces Round 929 (Div. 3) A. Turtle Puzzle: Rearrange and Negate 题意&#xff1a;可以对整数数组进行两个操作&#xff0c;一是随意重新排列或保持不变&#xff0c;二是选择连续子段元素符号倒转&#xff0c;求可能最大的所有元素和…

nginx反向代理之缓存 客户端IP透传 负载均衡

一 缓存功能 缓存功能可以加速访问&#xff0c;如果没有缓存关闭后端服务器后&#xff0c;图片将无法访问&#xff0c;缓存功能默认关闭&#xff0c;需要开启。 相关选项&#xff1a; ​ proxy_cache zone_name | off; 默认off #指明调用的缓存&#xff0c;或关闭缓存机制;C…

opencv中两个LSD直线检测算法的区别与应用

opencv中两个LSD直线检测算法的区别与应用 同样是Line Segment Detector(lsd)算法&#xff0c;opencv中提供了两种实现&#xff0c;并且位于不同的模块。下面分别介绍它们的使用方法&#xff1a; 1. LineSegmentDetector 由于源码许可证问题 OpenCV 3.4.6-3.4.15、4.1.0-4.5.…

关于uniapp小程序的分包问题

开发uniapp小程序时&#xff0c;在打包上传代码时会出现超出2M的打包限制不能上传&#xff0c;那么我们该怎么做呢&#xff1f; 1.对于图片&#xff0c;将图片从后端服务取&#xff0c;尽量不要放在静态资源&#xff0c;图片体积会影响打包大小。 2.使用分包&#xff0c;tabb…

蓝桥杯_中断系统

一 中断 中断&#xff0c;即cpu暂停执行当前程序&#xff0c;转而执行另外一段特殊程序&#xff0c;处理结束后。返回之前暂停程序继续执行。 中断向量&#xff0c;中断服务程序的入口地址&#xff0c;每个中断源都对应一个固定的入口地址。 中断服务函数&#xff0c;内核响应中…

华为s5720s-28p-power-li-ac堆叠配置

叠物理约束&#xff1a; • 连线推荐示意图选用产品子系列中固定的一款设备做示例&#xff0c;与选择产品时指定型号的外观可能不同。示意图主要用于让用户了解相同子系列设备可以用作堆叠的端口的位置&#xff0c;以及使用不同的连线方式时如何连接设备上的端口。因此&#xf…

解决vscode内置视图npm脚本操作报权限问题

项目背景 当我们使用 vscode 运行NPM脚本时却爆红了&#xff0c;提示系统上禁止运行脚本。 解决思路 竟然提示权限不够&#xff0c;那么咱们就从系统权限出发&#xff0c;vscode右键以管理员身份运行 在集成终端中输入一下命令 # get-executionpolicy是PowerShell中的命令,用…

推荐5个python可视化库

你是否曾为数据可视化而烦恼&#xff1f; 在浩瀚的数据海洋中&#xff0c;如何将复杂的数据以直观、易懂的方式展现出来&#xff0c;成为了每个数据分析师和开发者必须面对的挑战。 幸运的是&#xff0c;我们有众多强大的可视化工具可以选择。 推荐5个Python可视化库&#x…

JS画摆线

最近看到一个很漂亮的曲线&#xff0c;研究了一下。 从圆心画一条线匀速转动&#xff0c;终点再画一条线转动&#xff0c;2条线转速不同&#xff0c;会画出很漂亮的花纹。 一个周期 完整周期 <html> <style> body { background:black; } p { text-align:center; c…