基于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;//边为顶点减去1if (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;//边为顶点减去1if (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}}

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

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

相关文章

前缀和——724. 寻找数组的中心下标

文章目录 &#x1f353;1. 题目&#x1fad2;2. 算法原理&#x1f984;解法一&#xff1a;暴力枚举&#x1f984;解法二&#xff1a;前缀和 &#x1f954;3. 代码实现 &#x1f353;1. 题目 题目链接&#xff1a;724. 寻找数组的中心下标 - 力扣&#xff08;LeetCode&#xff0…

排查光模块故障原因,少不了这2条命令!

光模块故障定位常用命令 根据光模块的告警信息查找故障原因&#xff1a; display interface transceiver查看光模块光功率是否正常 display interface transceiver verbose根据光模块的告警信息查找故障原因 执行命令display interface transceiver查看“Alarm information”…

传统制造业如何有效实现数字化?企业数字化转型可以借助哪些工具?

2020年&#xff0c;制造业数字化转型行业的价值为2630亿美元。然而&#xff0c;到2026年&#xff0c;这一数字预计将达到惊人的7670亿美元。随着新技术的出现&#xff0c;工业4.0时代已经开始。这是由软件开发和自动化带来的对数字化的日益依赖所决定的&#xff0c;这使得制造过…

python获取json所有节点和子节点

使用python获取json的所有父结点和子节点 并使用父节点加下划线命名子节点 先展示一段json代码 {"level1": {"level2": {"level3": [{"level4": "4value"},{"level4_2": "4_2value"}]},"level2_…

深入剖析预约上门服务系统源码:构建高效服务的代码之旅

在本文中&#xff0c;我们将深入研究预约上门服务系统的源码&#xff0c;透过代码的层层剖析&#xff0c;揭示系统背后的技术奥秘。我们将关注系统的核心功能&#xff0c;并通过代码示例演示其实现过程&#xff0c;为读者提供一个深度技术解读的体验。 1. 技术栈选择&#xf…

plantUML学习与实战

背景 在日常工作或者生活中&#xff0c;使用交互图来描述想法&#xff0c;往往相对于文字来说&#xff0c;可读性更高&#xff0c;同时一定程度上可以提高沟通效率&#xff0c;但是苦于&#xff0c;不想对一堆控件拖拖拉拉&#xff0c;本人就是一个很讨厌画图&#xff0c;但是…

java学习part09类的构造器

1. 2.默认构造器 如果没有显式定义任何构造器&#xff0c;系统会默认加一个默认构造器。 如果定义了&#xff0c;则不会有默认构造器。 默认构造器的权限和类的权限一样&#xff0c;类是public构造器就是public&#xff0c;类是缺省默认构造器就是缺省 反编译之后添加的构造…

Astute Graphics 2023(ai创意插件合集)

Astute Graphics 2023是一家专注于图形编辑软件的公司&#xff0c;以制作高质量、功能强大的图像编辑工具而闻名。如Poser Pro、Poser 3D、Smart Shapes、Astute Sketch Pro等。 Astute Graphics的软件具有以下特点&#xff1a; 强大的图像编辑功能&#xff1a;Astute Graphi…

动态规划求二维网格中从左上角到右下角的最短路径( 每次只能向下、向右、向右下走 ) java 实现

dp[i][j] 表示在以点(0,0)作为左上角&#xff0c;点(i,i) 作为右下角的二维网格中 左上角到右下角的最短路径&#xff0c; 动态转移方程为&#xff1a;dp[i][j] min{ dp[i][j-1],dp[i-1][j],dp[i-1][j-1] }.distance weight[i][j] ImageUtils.java&#xff1a; import java.a…

时间序列分析算法的概念、模型检验及应用

时间序列分析是一种用于研究随时间变化的数据模式和趋势的统计方法。这类数据通常按照时间顺序排列&#xff0c;例如股票价格、气温、销售额等。时间序列分析的目标是从过去的观测中提取信息&#xff0c;以便预测未来的趋势。 以下是关于时间序列分析的一些重要概念、模型检验…

蓝桥杯算法双周赛心得——迷宫逃脱(dp)

大家好&#xff0c;我是晴天学长&#xff0c;dp版的来啦&#xff0c;可以是受益匪浅啊&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .迷宫逃脱 迷官逃脱[算法赛] 问题描述 在数学王国中&#xff0c;存…

【办公常识_1】写好的代码如何上传?使用svn commit

首先找到对应的目录 找到文件之后点击SVN Commit