最小生成树算法:Prim 算法、Kruskal 算法

最小生成树算法

最小生成树(Minimum Spanning Tree,MST)是图论中一个重要的概念,表示连接图中所有顶点的树,同时保证总权值最小。

比较:

  1. 贪心策略的不同

    • Prim 算法是一种顶点驱动的贪心算法,从一个初始顶点开始,每次选择距离当前生成树最近的顶点,并将其加入生成树中。这意味着 Prim 算法每次都是在已经形成的生成树上加入一个新的顶点,直到所有顶点都被加入。
    • Kruskal 算法是一种边驱动的贪心算法,它首先将所有边按照权值从小到大排序,然后依次选择权值最小的边,如果选择的边不会形成环路,就将其加入生成树中。这意味着 Kruskal 算法是在所有边的集合上进行操作,直到生成树中包含了所有顶点。
  2. 数据结构的不同

    • Prim 算法通常使用优先队列或最小堆来实现,以快速找到距离当前生成树最近的顶点。
    • Kruskal 算法通常使用并查集来检测是否形成环路,以保证生成树的连通性。
  3. 时间复杂度

    • 在稠密图(边数量接近顶点数量的平方)上,Prim 算法的时间复杂度为 O(V^2),其中 V 是顶点的数量。
    • 在稀疏图(边数量远小于顶点数量的平方)上,Prim 算法的时间复杂度可以优化到 O(E log V),其中 E 是边的数量。
    • Kruskal 算法的时间复杂度为 O(E log E),其中 E 是边的数量,因为它需要对所有边进行排序。
  4. 适用性

    • 当图是稠密图时,Prim 算法通常比较适用,因为它的时间复杂度不受边的数量的影响。
    • 当图是稀疏图时,Kruskal 算法通常比较适用,因为它的时间复杂度与边的数量相关,而稀疏图的边数量相对较少。

Prim算法

Prim算法是一种贪心算法,它从一个初始顶点开始,逐步扩展生成树,每次选择与当前树相邻的权值最小的边加入。

算法步骤:

  1. 选择一个初始顶点作为生成树的根节点。
  2. 初始化一个空的生成树集合和一个优先队列(或最小堆),将初始顶点及其相邻边加入优先队列。
  3. 从优先队列中选择权值最小的边,将其加入生成树集合,并将其相邻的顶点及边加入优先队列。
  4. 重复步骤3,直到生成树包含所有顶点。

代码示例:

class PriorityQueue {/*** 优先队列构造函数*/constructor() {// 初始化队列为空数组this.queue = [];}/*** 将顶点及其权重添加到优先队列* @param {string} vertex - 顶点* @param {number} weight - 权重*/enqueue(vertex, weight) {this.queue.push({ vertex, weight });this.sort();}/*** 从优先队列中删除并返回顶点及其权重* @returns {Object} - 包含顶点及其权重的对象*/dequeue() {return this.queue.shift();}/*** 对队列进行排序*/sort() {this.queue.sort((a, b) => a.weight - b.weight);}/*** 检查队列是否为空* @returns {boolean} - 如果队列为空则返回true,否则返回false*/isEmpty() {return this.queue.length === 0;}
}/*** 使用Prim算法查找最小生成树* @param {Object} graph - 表示图的邻接表* @returns {Array} - 包含最小生成树的边的数组*/
function prim(graph) {const visited = {};const mst = [];const startVertex = Object.keys(graph)[0];const priorityQueue = new PriorityQueue();visited[startVertex] = true;// 将起始顶点的所有相邻顶点及权重添加到优先队列for (const neighbor in graph[startVertex]) {const weight = graph[startVertex][neighbor];priorityQueue.enqueue(neighbor, weight);}// 遍历优先队列,直到队列为空while (!priorityQueue.isEmpty()) {const { vertex, weight } = priorityQueue.dequeue();if (!visited[vertex]) {visited[vertex] = true;// 将顶点、权重和起始顶点添加到最小生成树中mst.push({ from: startVertex, to: vertex, weight });// 将顶点的所有未访问相邻顶点及其权重添加到优先队列for (const neighbor in graph[vertex]) {if (!visited[neighbor]) {const weight = graph[vertex][neighbor];priorityQueue.enqueue(neighbor, weight);}}}}return mst;
}// 示例图
const graph = {A: { B: 2, D: 3 },B: { A: 2, C: 1, D: 1 },C: { B: 1, D: 4, E: 5 },D: { A: 3, B: 1, C: 4, E: 1 },E: { C: 5, D: 1 }
};const minimumSpanningTree = prim(graph);
console.log(minimumSpanningTree);

Kruskal算法

Kruskal算法是一种贪心算法,它首先将所有边按照权值从小到大进行排序,然后依次选择权值最小且不形成环的边加入生成树,直到生成树包含所有顶点。

算法步骤:

  1. 将图中所有边按照权值从小到大进行排序。
  2. 初始化一个空的生成树集合。
  3. 依次选择排序后的边,如果该边的两个顶点不在同一个连通分量中,则将该边加入生成树,并合并两个连通分量。
  4. 重复步骤3,直到生成树包含所有顶点。

代码示例:

class DisjointSet {/*** 创建一个新的并查集* @param {number} n - 初始大小*/constructor(n) {this.parent = new Array(n).fill(-1);}/*** 查找元素所属的集合* @param {number} x - 要查找的元素* @returns {number} - 元素所属的集合的根节点*/find(x) {if (this.parent[x] < 0) return x;return this.parent[x] = this.find(this.parent[x]);}/*** 合并两个集合* @param {number} x - 第一个元素* @param {number} y - 第二个元素* @returns {boolean} - 如果两个元素属于不同的集合,则返回true;否则返回false*/union(x, y) {// 查找节点x的根节点const rootX = this.find(x);// 查找节点y的根节点const rootY = this.find(y);// 如果两个节点不是同一个根节点if (rootX !== rootY) {// 如果节点x的根节点的父节点小于节点y的根节点的父节点if (this.parent[rootX] < this.parent[rootY]) {// 将节点x的根节点的父节点值加上节点y的根节点的父节点值this.parent[rootX] += this.parent[rootY];// 将节点y的根节点的父节点设置为节点x的根节点this.parent[rootY] = rootX;// 如果节点y的根节点的父节点小于等于节点x的根节点的父节点} else {// 将节点y的根节点的父节点值加上节点x的根节点的父节点值this.parent[rootY] += this.parent[rootX];// 将节点x的根节点的父节点设置为节点y的根节点this.parent[rootX] = rootY;}// 返回true,表示合并成功return true;}// 如果两个节点是同一个根节点,表示已经在同一个集合中,不需要合并return false;}
}/**
* 使用Kruskal算法查找最小生成树
* @param {Object} graph - 表示图的邻接表
* @returns {Array} - 包含最小生成树的边的数组
*/
function kruskal(graph) {// 存储边的数组const edges = [];// 存储顶点的数组const vertices = Object.keys(graph);// 创建一个并查集const disjointSet = new DisjointSet(vertices.length);// 存储最小生成树的边const mst = [];// 遍历图中的所有边,并将它们添加到边数组中// 将所有边添加到边数组中for (const from in graph) {for (const to in graph[from]) {edges.push({ from, to, weight: graph[from][to] });}}// 对边数组进行排序,按照边的权重从小到大排序// 根据权重对边数组进行排序edges.sort((a, b) => a.weight - b.weight);// 遍历排序后的边数组,依次加入最小生成树中for (const edge of edges) {const { from, to, weight } = edge;// 如果两个顶点不在同一个集合中,则加入最小生成树,并合并两个集合if (disjointSet.union(vertices.indexOf(from), vertices.indexOf(to))) {mst.push(edge);}}// 返回最小生成树return mst;
}// 示例图
const graph = {A: { B: 2, D: 3 },B: { A: 2, C: 1, D: 1 },C: { B: 1, D: 4, E: 5 },D: { A: 3, B: 1, C: 4, E: 1 },E: { C: 5, D: 1 }
};const minimumSpanningTree = kruskal(graph);
console.log(minimumSpanningTree);

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

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

相关文章

力扣中档题的简单写法:在链表中插入最大公约数

其实暴力遍历开数组也可以&#xff0c;但不如以下新建链表块的方法简单 int FindCommDivisor(int num1, int num2) {int n;int i;n fmin(num1, num2);for (i n; i > 1; i--) {if (num1 % i 0 && num2 % i 0) {return i;}}return 0; }struct ListNode *insertGr…

Mock.js 基本语法与应用笔记

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

python 导入excel空间三维坐标 生成三维曲面地形图 5-3、线条平滑曲面且可通过面观察柱体变化(三)

环境 python:python-3.12.0-amd64 包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata from matplotlib.c…

【SSM】整合原理和配置实战

文章目录 SSM整合是什么&#xff1f;SSM整合核心问题第一问&#xff1a;SSM整合需要几个IoC容器&#xff1f;第二问&#xff1a;每个IoC容器对应哪些类型组件&#xff1f;第三问&#xff1a;IoC容器之间关系和调用方向&#xff1f;第四问&#xff1a;具体多少配置类以及对应容器…

力扣hot100:22.括号生成(回溯)

复习一下&#xff1a; 回溯法解决的问题都可以抽象为树形结构。回溯法解决的都是在集合中递归查找子集&#xff0c;集合的大小就构成了树的宽度&#xff0c;递归的深度&#xff0c;都构成的树的深度。 对于同一层而言&#xff0c;其儿子都是等价的不同情况&#xff0c;因此当儿…

【Poe】保姆级注册教程

AI聊天机器人已成为技术界的热点。Quora推出了其全新的AI聊天机器人应用——poe&#xff0c;为用户提供了一种新的与人工智能进行互动的方式。与其他常见的AI聊天机器人不同&#xff0c;poe支持多家公司的AI系统&#xff0c;例如OpenAI的ChatGPT和Anthropic的聊天机器人。本教程…

【零基础学习01】嵌入式linux驱动中pinctrl和gpio子系统实现

大家好,为了进一步提升大家对实验的认识程度,每个控制实验将加入详细控制思路与流程,欢迎交流学习。 今天给大家分享一下,linux系统里面pinctrl和gpio子系统控制实验,操作硬件为I.MX6ULL开发板。 第一:pinctrl和gpio子系统简介 Linux系统是一个庞大又完善的系统,如果采用…

Window部署Oracle并实现公网环境远程访问本地数据库

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…

基于单片机的机动车智能远光灯系统设计

目 录 摘 要 I Abstract II 引 言 1 1 主要研究内容及总体设计方案 3 1.1 主要研究内容 3 1.2 系统总体方案选择 3 1.3 系统功能的确定 4 2 硬件电路的设计 5 2.1 单片机控制模块设计 5 2.2 液晶显示模块电路设计 7 2.3 远近灯光电路设计 9 2.4 按键电路设计 9 2.5 超声波电路…

5G与智慧文旅的融合发展:推动旅游业转型升级与可持续发展

随着5G技术的飞速发展和广泛应用&#xff0c;其与智慧文旅的融合发展正成为推动旅游业转型升级与可持续发展的重要力量。5G技术以其高速率、低时延、大连接的特性&#xff0c;为智慧文旅注入了新的活力&#xff0c;助力旅游业实现更高效、更智能、更绿色的发展。本文将深入探讨…

保持长期高效的七个法则(一)7 Rules for Staying Productive Long-Term(1)

Easily the best habit I’ve ever started was to use a productivity system.The idea is simple:organizing all the stuff you need to do (and how you’re going to do it) prevents a lot of internal struggle to get things done. 无疑&#xff0c;我曾经建立过的最好…

AI辅助研发

随着人工智能技术的持续发展与突破&#xff0c;2024年AI辅助研发正成为科技界和工业界瞩目的焦点。从医药研发到汽车设计&#xff0c;从软件开发到材料科学&#xff0c;AI正逐渐渗透到研发的各个环节&#xff0c;变革着传统的研发模式。在这一背景下&#xff0c;AI辅助研发不仅…

【初始MongoDB】MongoDB的使用(对比MySQL)

MongoDB简介 1、NoSQL简介 NoSQL(NoSQL Not Only SQL)&#xff0c;意即反SQL运动&#xff0c;指的是非关系型的数据库&#xff0c;是一项全新的数据库革命性运动&#xff0c;早期就有人提出&#xff0c;发展至2009年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储…

Qt 数据库驱动未装载MYSQL

一、第一部分 0.Qt 连接mysql数据库时报错&#xff1a; QSqlDatabase: QMYSQL driver not loaded QSqlDatabase: available drivers: QSQLITE QODBC QODBC3 QPSQL QPSQL7 QT连接代码&#xff1a; bool createMysqlConn() {QSqlDatabase sqldb QSqlDatabase::addDatabase(&qu…

如何配置固定TCP公网地址实现远程访问内网MongoDB数据库

文章目录 前言1. 安装数据库2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射2.3 测试随机公网地址远程连接 3. 配置固定TCP端口地址3.1 保留一个固定的公网TCP端口地址3.2 配置固定公网TCP端口地址3.3 测试固定地址公网远程访问 前言 MongoDB是一个基于分布式文件存储的数…

Java建造者模式源码剖析及使用场景

一、介绍 Java 中的建造者模式(Builder Pattern)是一种创建型设计模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。该模式主要用于创建一些复杂的对象,这些对象内部由多个部分组成,各部分之间存在着复杂的相互依赖关系。 二、有什么好处&am…

JVM工作原理与实战(四十三):JVM常见面试题目

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、JVM常见面试题目 1.什么是类加载器&#xff0c;有哪些常见的类加载器&#xff1f; 2.什么是双亲委派机制&#xff0c;以及如何打破双亲委派机制&#xff1f; 3.如何判断堆上的对…

Hive超市零售案例

超市零售案例 一、部分数据展示 Fiskars 剪刀| 蓝色,61,中国,华东,杭州,用品,曾惠,2,浙江,办公用品,US-2019-1357144,130 GlobeWeis 搭扣信封| 红色,43,中国,西南,内江,信封,许安,2,四川,办公用品,CN-2019-1973789,125 Cardinal 孔加固材料| 回收,4,中国,西南,内江,装订机,许…

Unity性能优化篇(十) 模型优化之网格合并 Easy Mesh Combine Tool插件使用以及代码实现网格合并

把多个模型的网格合并为一个网格。可以使用自己写代码&#xff0c;使用Unity自带的CombineMeshes方法&#xff0c;也可以使用资源商店的插件&#xff0c;在资源商店搜Mesh Combine可以搜索到相关的插件&#xff0c;例如Easy Mesh Combine Tool等插件。 可大幅度减少Batches数量…

css flex 布局换行

默认使用display: flex;是不换行的&#xff0c;只需要加上flex-wrap: wrap;就行了&#xff0c;效果图 .app-center {display: flex;flex-wrap: wrap;justify-content:flex-start; } 通过上面我们发现虽然时间换行了&#xff0c;但是每行的边距不一样 加上这个就行了&#xff…