基于C#实现Prim算法

图论在数据结构中是非常有趣而复杂的,作为 Web 码农的我,在实际开发中一直没有找到它的使用场景,不像树那样的频繁使用,不过还是准备仔细的把图论全部过一遍。

一、最小生成树

图中有一个好玩的东西叫做生成树,就是用边来把所有的顶点联通起来,前提条件是最后形成的联通图中不能存在回路,所以就形成这样一个推理:假设图中的顶点有 n 个,则生成树的边有 n-1 条,多一条会存在回路,少一路则不能把所有顶点联通起来,如果非要在图中加上权重,则生成树中权重最小的叫做最小生成树。
image.png
对于上面这个带权无向图来说,它的生成树有多个,同样最小生成树也有多个,因为我们比的是权重的大小。

二、Prim 算法

求最小生成树的算法有很多,常用的是 Prim 算法和 Kruskal 算法,为了保证单一职责,我把 Kruskal 算法放到下一篇,那么 Prim 算法的思想是什么呢?很简单,贪心思想。
如上图:现有集合 M={A,B,C,D,E,F},再设集合 N={}。

  • 第一步:挑选任意节点(比如 A),将其加入到 N 集合,同时剔除 M 集合的 A。
  • 第二步:寻找 A 节点权值最小的邻节点(比如 F),然后将 F 加入到 N 集合,此时 N={A,F},同时剔除 M 集合中的 F。
  • 第三步:寻找{A,F}中的权值最小的邻节点(比如 E),然后将 E 加入到 N 集合,此时 N={A,F,E},同时剔除 M 集合的 E。
  • 。。。

最后 M 集合为{}时,生成树就构建完毕了,是不是非常的简单,这种贪心做法我想大家都能想得到,如果算法配合一个好的数据结构,就会如虎添翼。

三、代码

1、图的存储

图的存储有很多方式,邻接矩阵,邻接表,十字链表等等,当然都有自己的适合场景,下面用邻接矩阵来玩玩,邻接矩阵需要采用两个数组,
①. 保存顶点信息的一维数组,
②. 保存边信息的二维数组。

 public class Graph
 {
     /// <summary>
     /// 顶点个数
     /// </summary>
     public char[] vertexs;

     /// <summary>
     /// 边的条数
     /// </summary>
     public int[,] edges;

     /// <summary>
     /// 顶点个数
     /// </summary>
     public int vertexsNum;

     /// <summary>
     /// 边的个数
     /// </summary>
     public int edgesNum;
 }

2、矩阵构建

矩阵构建很简单,这里把上图中的顶点和权的信息保存在矩阵中。

 #region 矩阵的构建
 /// <summary>
 /// 矩阵的构建
 /// </summary>
 public void Build()
 {
     //顶点数
     graph.vertexsNum = 6;

     //边数
     graph.edgesNum = 8;

     graph.vertexs = new char[graph.vertexsNum];

     graph.edges = new int[graph.vertexsNum, graph.vertexsNum];

     //构建二维数组
     for (int i = 0; i < graph.vertexsNum; i++)
     {
         //顶点
         graph.vertexs[i] = (char)(i + 65);

         for (int j = 0; j < graph.vertexsNum; j++)
         {
             graph.edges[i, j] = int.MaxValue;
         }
     }

     graph.edges[0, 1] = graph.edges[1, 0] = 80;
     graph.edges[0, 3] = graph.edges[3, 0] = 100;
     graph.edges[0, 5] = graph.edges[5, 0] = 20;
     graph.edges[1, 2] = graph.edges[2, 1] = 90;
     graph.edges[2, 5] = graph.edges[5, 2] = 70;
     graph.edges[3, 2] = graph.edges[2, 3] = 100;
     graph.edges[4, 5] = graph.edges[5, 4] = 40;
     graph.edges[3, 4] = graph.edges[4, 3] = 60;
     graph.edges[2, 3] = graph.edges[3, 2] = 10;
 }
 #endregion

3、Prim

要玩 Prim,我们需要两个字典。
①:保存当前节点的字典,其中包含该节点的起始边和终边以及权值,用 weight=-1 来记录当前节点已经访问过,用 weight=int.MaxValue 表示两节点没有边。
②:输出节点的字典,存放的就是我们的 N 集合。
当然这个复杂度玩高了,为 O(N2),寻找 N 集合的邻边最小权值时,我们可以玩玩 AVL 或者优先队列来降低复杂度。

 #region prim算法
 /// <summary>
 /// prim算法
 /// </summary>
 public Dictionary<char, Edge> Prim()
 {
     Dictionary<char, Edge> dic = new Dictionary<char, Edge>();

     //统计结果
     Dictionary<char, Edge> outputDic = new Dictionary<char, Edge>();

     //weight=MaxValue:标识没有边
     for (int i = 0; i < graph.vertexsNum; i++)
     {
         //起始边
         var startEdge = (char)(i + 65);

         dic.Add(startEdge, new Edge() { weight = int.MaxValue });
     }

     //取字符的开始位置
     var index = 65;

     //取当前要使用的字符
     var start = (char)(index);

     for (int i = 0; i < graph.vertexsNum; i++)
     {
         //标记开始边已使用过
         dic[start].weight = -1;

         for (int j = 1; j < graph.vertexsNum; j++)
         {
             //获取当前 c 的 邻边
             var end = (char)(j + index);

             //取当前字符的权重
             var weight = graph.edges[(int)(start) - index, j];

             if (weight < dic[end].weight)
             {
                 dic[end] = new Edge()
                 {
                     weight = weight,
                     startEdge = start,
                     endEdge = end
                 };
             }
         }

         var min = int.MaxValue;

         char minkey = ' ';

         foreach (var key in dic.Keys)
         {
             //取当前 最小的 key(使用过的除外)
             if (min > dic[key].weight && dic[key].weight != -1)
             {
                 min = dic[key].weight;
                 minkey = key;
             }
         }

         start = minkey;

         //边为顶点减去1
         if (outputDic.Count < graph.vertexsNum - 1 && !outputDic.ContainsKey(minkey))
         {
             outputDic.Add(minkey, new Edge()
             {
                 weight = dic[minkey].weight,
                 startEdge = dic[minkey].startEdge,
                 endEdge = dic[minkey].endEdge
             });
         }
     }
     return outputDic;
 }
 #endregion

4、最后我们来测试一下,看看找出的最小生成树。

 public static void Main()
 {
     MatrixGraph martix = new MatrixGraph();

     martix.Build();

     var dic = martix.Prim();

     Console.WriteLine("最小生成树为:");

     foreach (var key in dic.Keys)
     {
         Console.WriteLine("({0},{1})({2})", dic[key].startEdge, dic[key].endEdge, dic[key].weight);
     }

     Console.Read();
 }

image.png

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Diagnostics;
 using System.Threading;
 using System.IO;
 using SupportCenter.Test.ServiceReference2;
 using System.Threading.Tasks;
 
 namespace ConsoleApplication2
 {
     public class Program
     {
         public static void Main()
         {
          MatrixGraph martix = new MatrixGraph();
 
             martix.Build();
 
             var dic = martix.Prim();
 
             Console.WriteLine("最小生成树为:");
 
             foreach (var key in dic.Keys)
             {
                 Console.WriteLine("({0},{1})({2})", dic[key].startEdge, dic[key].endEdge, dic[key].weight);
             }
 
             Console.Read();
         }
     }
 
     /// <summary>
     /// 定义矩阵节点
     /// </summary>
     public class MatrixGraph
     {
         Graph graph = new Graph();
 
         public class Graph
         {
             /// <summary>
             /// 顶点个数
             /// </summary>
             public char[] vertexs;
 
             /// <summary>
             /// 边的条数
             /// </summary>
             public int[,] edges;
 
             /// <summary>
             /// 顶点个数
             /// </summary>
             public int vertexsNum;
 
             /// <summary>
             /// 边的个数
             /// </summary>
             public int edgesNum;
         }
 
         #region 矩阵的构建
         /// <summary>
         /// 矩阵的构建
         /// </summary>
         public void Build()
         {
             //顶点数
             graph.vertexsNum = 6;
 
             //边数
             graph.edgesNum = 8;
 
             graph.vertexs = new char[graph.vertexsNum];
 
             graph.edges = new int[graph.vertexsNum, graph.vertexsNum];
 
             //构建二维数组
             for (int i = 0; i < graph.vertexsNum; i++)
             {
                 //顶点
                 graph.vertexs[i] = (char)(i + 65);
 
                 for (int j = 0; j < graph.vertexsNum; j++)
                 {
                     graph.edges[i, j] = int.MaxValue;
                 }
             }
 
             graph.edges[0, 1] = graph.edges[1, 0] = 80;
             graph.edges[0, 3] = graph.edges[3, 0] = 100;
             graph.edges[0, 5] = graph.edges[5, 0] = 20;
             graph.edges[1, 2] = graph.edges[2, 1] = 90;
             graph.edges[2, 5] = graph.edges[5, 2] = 70;
             graph.edges[3, 2] = graph.edges[2, 3] = 100;
             graph.edges[4, 5] = graph.edges[5, 4] = 40;
             graph.edges[3, 4] = graph.edges[4, 3] = 60;
             graph.edges[2, 3] = graph.edges[3, 2] = 10;
         }
         #endregion
 
         #region 边的信息
         /// <summary>
         /// 边的信息
         /// </summary>
         public class Edge
         {
             //开始边
             public char startEdge;
 
             //结束边
             public char endEdge;
 
             //权重
             public int weight;
         }
         #endregion
 
         #region prim算法
         /// <summary>
         /// prim算法
         /// </summary>
         public Dictionary<char, Edge> Prim()
         {
             Dictionary<char, Edge> dic = new Dictionary<char, Edge>();
 
             //统计结果
             Dictionary<char, Edge> outputDic = new Dictionary<char, Edge>();
 
             //weight=MaxValue:标识没有边
             for (int i = 0; i < graph.vertexsNum; i++)
             {
                 //起始边
                 var startEdge = (char)(i + 65);
 
                 dic.Add(startEdge, new Edge() { weight = int.MaxValue });
             }
 
             //取字符的开始位置
             var index = 65;
 
             //取当前要使用的字符
             var start = (char)(index);
 
             for (int i = 0; i < graph.vertexsNum; i++)
             {
                 //标记开始边已使用过
                 dic[start].weight = -1;
 
                 for (int j = 1; j < graph.vertexsNum; j++)
                 {
                     //获取当前 c 的 邻边
                     var end = (char)(j + index);
 
                     //取当前字符的权重
                     var weight = graph.edges[(int)(start) - index, j];
 
                     if (weight < dic[end].weight)
                     {
                         dic[end] = new Edge()
                         {
                             weight = weight,
                             startEdge = start,
                             endEdge = end
                         };
                     }
                 }
 
                 var min = int.MaxValue;
 
                 char minkey = ' ';
 
                 foreach (var key in dic.Keys)
                 {
                     //取当前 最小的 key(使用过的除外)
                     if (min > dic[key].weight && dic[key].weight != -1)
                     {
                         min = dic[key].weight;
                         minkey = key;
                     }
                 }
 
                 start = minkey;
 
                 //边为顶点减去1
                 if (outputDic.Count < graph.vertexsNum - 1 && !outputDic.ContainsKey(minkey))
                 {
                     outputDic.Add(minkey, new Edge()
                     {
                         weight = dic[minkey].weight,
                         startEdge = dic[minkey].startEdge,
                         endEdge = dic[minkey].endEdge
                     });
                 }
             }
             return outputDic;
         }
         #endregion
     }
 }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis并发问题解决方案

目录 前言 1.分布式锁 1.基于单个节点 2.基于多个节点 3.watch(乐观锁) 2.原子操作 1.单命令操作 2.Lua 脚本(多命令操作) 3.事务 1.执行步骤 2.错误处理 3.崩溃处理 总结 前言 在多个客户端并发访问Redis的时候&#xff0c;虽然Redis是单线程执行指令&#xff…

基于IDEA+HTML+SpringBoot前后端分离电子商城

基于springboot的电子商城 项目介绍&#x1f481;&#x1f3fb; •B2C 商家对客户 •C2B2C 客户对商家对客户 1.1.1 B2C 平台运营方即商品的卖家 小米商城 •商品 •用户 1.1.2 C2B2C 平台运营方不卖商品&#xff08;也可以卖&#xff09; 卖家是平台的用户 买家也是平台用户 •…

【算法】经典算法题

文章目录 专题一&#xff1a;双指针1. 移动零2. 复写零3. 快乐数4. 盛最多水的容器5. 有效三角形的个数6. 查找总价格为目标值的两个商品7. 三数之和8. 四数之和 专题二&#xff1a;滑动窗口1. 长度最小的子数组2. 无重复字符的最长字串3. 最大连续1的个数 III4. 将 x 减到 0 的…

请你说一下Vue中v-if和v-for的优先级谁更高

v-if 与 v-for简介 v-ifv-forv-if & v-for使用 v-if 与 v-for优先级比较 vue2 中&#xff0c;v-for的优先级高于v-if 例子进行分析 vue3 v-if 具有比 v-for 更高的优先级 例子进行分析 总结 在vue2中&#xff0c;v-for的优先级高于v-if在vue3中&#xff0c;v-if的优先级高…

61 权限提升-RedisPostgre令牌窃取进程注入

目录 演示案例:Redis数据库权限提升-计划任务PostgreSQL数据库权限提升Windows2008&7令牌窃取提升-本地Windows2003&10进程注入提升-本地pinjector进程注入工具针对-win2008以前操作系统pexec64 32进程注入工具针对-win2008及后操作系统- (佛系) 涉及资源: postgersql是…

2023亚太杯数学建模APMCM竞赛C题思路讲解:基于ARIMA与机理模型进行预测

本文针对6大问题,从多角度分析了我国新能源电动汽车发展形势与前景。文中针对不同问题,采用了层次分析法、时间序列模型、机理模型、回归模型等数学方法。并结合实例数据,对相关模型进行求解,以量化预测了新能源电动汽车在政策驱动、市场竞争、温室气体减排等多个方面的潜在贡献…

OpenCV快速入门:图像分析——图像分割和图像修复

文章目录 前言一、图像分割1.1 漫水填充法1.1.1 漫水填充法原理1.1.2 漫水填充法实现步骤1.1.3 代码实现 1.2 分水岭法1.2.1 分水岭法原理1.2.2 分水岭法实现步骤1.2.3 代码实现 1.3 GrabCut法1.3.1 GrabCut法原理1.3.2 GrabCut法实现步骤1.3.3 代码实现 1.4 Mean-Shift法1.4.1…

【分布式】小白看Ring算法 - 03

相关系列 【分布式】NCCL部署与测试 - 01 【分布式】入门级NCCL多机并行实践 - 02 【分布式】小白看Ring算法 - 03 【分布式】大模型分布式训练入门与实践 - 04 概述 NCCL&#xff08;NVIDIA Collective Communications Library&#xff09;是由NVIDIA开发的一种用于多GPU间…

Navicat 技术指引 | 适用于 GaussDB 的数据迁移工具

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对 GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…

基于element-ui后台模板,日常唠嗑

后面会补充github地址 文章目录 目录 文章目录 案例说明 1.引入库 2.创建布局组件 3.创建布局组件 4.菜单效果展示 5.创建顶部组件 5.创建顶部面包屑组件 6.创建内容区域组件 7.效果总览 7.布丁&#xff08;实现一些小细节&#xff09; 前言一、pandas是什么&#xff1f;二、使…

Android Studio记录一个错误:Execution failed for task ‘:app:lintVitalRelease‘.

Android出现Execution failed for task :app:lintVitalRelease.> Lint found fatal errors while assembling a release target. Execution failed for task :app:lintVitalRelease解决方法 Execution failed for task ‘:app:lintVitalRelease’ build project 可以正常执…

计算机网络之网络层

一、概述 主要任务是实现网络互连&#xff0c;进而实现数据包在各网络之间的传输 1.1网络引入的目的 从7层结构上看&#xff0c;网络层下是数据链路层 从4层结构上看&#xff0c;网络层下面是网络接口层 至少我们看到的网络层下面是以太网 以太网解决了什么问题&#xff1f; 答…

python中一个文件(A.py)怎么调用另一个文件(B.py)中定义的类AA详解和示例

本文主要讲解python文件中怎么调用另外一个py文件中定义的类&#xff0c;将通过代码和示例解读&#xff0c;帮助大家理解和使用。 目录 代码B.pyA.py 调用过程 代码 B.py 如在文件B.py,定义了类别Bottleneck&#xff0c;其包含卷积层、正则化和激活函数层&#xff0c;主要对…

微信小程序实现【点击 滑动 评分 评星(5星)】功能

wxml文件&#xff1a; <view class"wxpl_xing"><view class"manyidu">{{scoreContent}}</view><view><block wx:for{{scoreArray}} wx:for-item"item"><view classstarLen bindtapchangeScore data-sy"{{…

什么是LLC电路?

LLC电路是由2个电感和1个电容构成的谐振电路&#xff0c;故称之为LLC&#xff1b; LLC电路主要由三个元件组成&#xff1a;两个电感分别为变压器一次侧漏感(Lr)和励磁电感(Lm)&#xff0c;电容为变压器一次侧谐振电容(Cr)。这些元件构成了一个谐振回路&#xff0c;其中输入电感…

程序员进阶高管指南,看懂工资最少加5k

从象牙塔毕业跨入社会大染缸&#xff0c;很多人都跟我谈过他们的职业困惑&#xff0c;其中有一些刚刚毕业&#xff0c;有些人已经工作超过10年。基本上是围绕着怎样持续提升&#xff0c;怎样晋升为高级管理者。那么这篇文章&#xff0c;我就来谈一谈程序员到高管的跃升之路。 …

程序环境和预处理(详解版)

我们已经学到这里&#xff0c;这就是关于C语言的最后一个集中的知识点了&#xff0c;虽然它比较抽象&#xff0c;但是了解这部分知识&#xff0c;可以让我们对C代码有更深层次的理解&#xff0c;知道代码在每一个阶段发生什么样的变化。让我们开始学习吧! 目录 1.程序的翻译环…

5个免费在线工具推荐

NSDT 三维场景建模工具GLTF/GLB在线编辑器Three.js AI自动纹理化开发包YOLO 虚幻合成数据生成器3D模型在线转换 1、NSDT 三维场景建模 访问地址&#xff1a;NSDT 编辑器 2、GLTF/GLB在线编辑器 访问地址&#xff1a;GLTF 编辑器 3、Three.js AI自动纹理化开发包 图一为原始模…

C++类与对象(4)—日期类的实现

目录 一、类的创建和方法声明 二 、输出&运算符重载 三、检查合法性 1、获取对应年月的天数 2、初始化 四、实现加等和加操作 1、先写再写 2、先写再写 3、两种方式对比 五、实现自增和--自减 1、自增 2、自减 六、 实现减等和减操作 1、减等天数 2、加负数…

【数据结构/C++】线性表_双链表基本操作

#include <iostream> using namespace std; typedef int ElemType; // 3. 双链表 typedef struct DNode {ElemType data;struct DNode *prior, *next; } DNode, *DLinkList; // 初始化带头结点 bool InitDNodeList(DLinkList &L) {L (DNode *)malloc(sizeof(DNode))…