经典算法题每日演练——第十四题 Prim算法
图论在数据结构中是非常有趣而复杂的,作为web码农的我,在实际开发中一直没有找到它的使用场景,不像树那样的频繁使用,不过还是准备
仔细的把图论全部过一遍。
一:最小生成树
图中有一个好玩的东西叫做生成树,就是用边来把所有的顶点联通起来,前提条件是最后形成的联通图中不能存在回路,所以就形成这样一个
推理:假设图中的顶点有n个,则生成树的边有n-1条,多一条会存在回路,少一路则不能把所有顶点联通起来,如果非要在图中加上权重,则生成树
中权重最小的叫做最小生成树。
对于上面这个带权无向图来说,它的生成树有多个,同样最小生成树也有多个,因为我们比的是权重的大小。
二: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. 图的存储
图的存储有很多方式,邻接矩阵,邻接表,十字链表等等,当然都有自己的适合场景,下面用邻接矩阵来玩玩,邻接矩阵需要采用两个数组,
①. 保存顶点信息的一维数组,
②. 保存边信息的二维数组。
1 public class Graph
2 {
3 /// <summary>
4 /// 顶点个数
5 /// </summary>
6 public char[] vertexs;
7
8 /// <summary>
9 /// 边的条数
10 /// </summary>
11 public int[,] edges;
12
13 /// <summary>
14 /// 顶点个数
15 /// </summary>
16 public int vertexsNum;
17
18 /// <summary>
19 /// 边的个数
20 /// </summary>
21 public int edgesNum;
22 }
2:矩阵构建
矩阵构建很简单,这里把上图中的顶点和权的信息保存在矩阵中。
1 #region 矩阵的构建
2 /// <summary>
3 /// 矩阵的构建
4 /// </summary>
5 public void Build()
6 {
7 //顶点数
8 graph.vertexsNum = 6;
9
10 //边数
11 graph.edgesNum = 8;
12
13 graph.vertexs = new char[graph.vertexsNum];
14
15 graph.edges = new int[graph.vertexsNum, graph.vertexsNum];
16
17 //构建二维数组
18 for (int i = 0; i < graph.vertexsNum; i++)
19 {
20 //顶点
21 graph.vertexs[i] = (char)(i + 65);
22
23 for (int j = 0; j < graph.vertexsNum; j++)
24 {
25 graph.edges[i, j] = int.MaxValue;
26 }
27 }
28
29 graph.edges[0, 1] = graph.edges[1, 0] = 80;
30 graph.edges[0, 3] = graph.edges[3, 0] = 100;
31 graph.edges[0, 5] = graph.edges[5, 0] = 20;
32 graph.edges[1, 2] = graph.edges[2, 1] = 90;
33 graph.edges[2, 5] = graph.edges[5, 2] = 70;
34 graph.edges[3, 2] = graph.edges[2, 3] = 100;
35 graph.edges[4, 5] = graph.edges[5, 4] = 40;
36 graph.edges[3, 4] = graph.edges[4, 3] = 60;
37 graph.edges[2, 3] = graph.edges[3, 2] = 10;
38 }
39 #endregion
3:Prim
要玩Prim,我们需要两个字典。
①:保存当前节点的字典,其中包含该节点的起始边和终边以及权值,用weight=-1来记录当前节点已经访问过,用weight=int.MaxValue表示
两节点没有边。
②:输出节点的字典,存放的就是我们的N集合。
当然这个复杂度玩高了,为O(N2),寻找N集合的邻边最小权值时,我们可以玩玩AVL或者优先队列来降低复杂度。
1 #region prim算法
2 /// <summary>
3 /// prim算法
4 /// </summary>
5 public Dictionary<char, Edge> Prim()
6 {
7 Dictionary<char, Edge> dic = new Dictionary<char, Edge>();
8
9 //统计结果
10 Dictionary<char, Edge> outputDic = new Dictionary<char, Edge>();
11
12 //weight=MaxValue:标识没有边
13 for (int i = 0; i < graph.vertexsNum; i++)
14 {
15 //起始边
16 var startEdge = (char)(i + 65);
17
18 dic.Add(startEdge, new Edge() { weight = int.MaxValue });
19 }
20
21 //取字符的开始位置
22 var index = 65;
23
24 //取当前要使用的字符
25 var start = (char)(index);
26
27 for (int i = 0; i < graph.vertexsNum; i++)
28 {
29 //标记开始边已使用过
30 dic[start].weight = -1;
31
32 for (int j = 1; j < graph.vertexsNum; j++)
33 {
34 //获取当前 c 的 邻边
35 var end = (char)(j + index);
36
37 //取当前字符的权重
38 var weight = graph.edges[(int)(start) - index, j];
39
40 if (weight < dic[end].weight)
41 {
42 dic[end] = new Edge()
43 {
44 weight = weight,
45 startEdge = start,
46 endEdge = end
47 };
48 }
49 }
50
51 var min = int.MaxValue;
52
53 char minkey = ' ';
54
55 foreach (var key in dic.Keys)
56 {
57 //取当前 最小的 key(使用过的除外)
58 if (min > dic[key].weight && dic[key].weight != -1)
59 {
60 min = dic[key].weight;
61 minkey = key;
62 }
63 }
64
65 start = minkey;
66
67 //边为顶点减去1
68 if (outputDic.Count < graph.vertexsNum - 1 && !outputDic.ContainsKey(minkey))
69 {
70 outputDic.Add(minkey, new Edge()
71 {
72 weight = dic[minkey].weight,
73 startEdge = dic[minkey].startEdge,
74 endEdge = dic[minkey].endEdge
75 });
76 }
77 }
78 return outputDic;
79 }
80 #endregion
4:最后我们来测试一下,看看找出的最小生成树。
1 public static void Main()
2 {
3 MatrixGraph martix = new MatrixGraph();
4
5 martix.Build();
6
7 var dic = martix.Prim();
8
9 Console.WriteLine("最小生成树为:");
10
11 foreach (var key in dic.Keys)
12 {
13 Console.WriteLine("({0},{1})({2})", dic[key].startEdge, dic[key].endEdge, dic[key].weight);
14 }
15
16 Console.Read();
17 }
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 = ; //边数
graph.edgesNum = ; graph.vertexs = new char[graph.vertexsNum]; graph.edges = new int[graph.vertexsNum, graph.vertexsNum]; //构建二维数组
for (int i = ; i < graph.vertexsNum; i++)
{
//顶点
graph.vertexs[i] = (char)(i + ); for (int j = ; j < graph.vertexsNum; j++)
{
graph.edges[i, j] = int.MaxValue;
}
} graph.edges[, ] = graph.edges[, ] = ;
graph.edges[, ] = graph.edges[, ] = ;
graph.edges[, ] = graph.edges[, ] = ;
graph.edges[, ] = graph.edges[, ] = ;
graph.edges[, ] = graph.edges[, ] = ;
graph.edges[, ] = graph.edges[, ] = ;
graph.edges[, ] = graph.edges[, ] = ;
graph.edges[, ] = graph.edges[, ] = ;
graph.edges[, ] = graph.edges[, ] = ;
}
#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 = ; i < graph.vertexsNum; i++)
{
//起始边
var startEdge = (char)(i + ); dic.Add(startEdge, new Edge() { weight = int.MaxValue });
} //取字符的开始位置
var index = ; //取当前要使用的字符
var start = (char)(index); for (int i = ; i < graph.vertexsNum; i++)
{
//标记开始边已使用过
dic[start].weight = -; for (int j = ; 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 != -)
{
min = dic[key].weight;
minkey = key;
}
} start = minkey; //边为顶点减去1
if (outputDic.Count < graph.vertexsNum - && !outputDic.ContainsKey(minkey))
{
outputDic.Add(minkey, new Edge()
{
weight = dic[minkey].weight,
startEdge = dic[minkey].startEdge,
endEdge = dic[minkey].endEdge
});
}
}
return outputDic;
}
#endregion
}
}
经典算法题每日演练——第十四题 Prim算法的更多相关文章
- 经典算法题每日演练——第十六题 Kruskal算法
原文:经典算法题每日演练--第十六题 Kruskal算法 这篇我们看看第二种生成树的Kruskal算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的. 一:思想 若存在M={0, ...
- codeforces水题100道 第二十四题 Codeforces Beta Round #85 (Div. 2 Only) A. Petya and Strings (strings)
题目链接:http://www.codeforces.com/problemset/problem/112/A题意:忽略大小写,比较两个字符串字典序大小.C++代码: #include <cst ...
- codeforces水题100道 第十四题 Codeforces Round #321 (Div. 2) A. Kefa and First Steps (brute force)
题目链接:http://www.codeforces.com/problemset/problem/580/A题意:求最长连续非降子序列的长度.C++代码: #include <iostream ...
- 经典算法题每日演练——第十一题 Bitmap算法
原文:经典算法题每日演练--第十一题 Bitmap算法 在所有具有性能优化的数据结构中,我想大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量时间,多么的简洁优美, 但是在特定的场 ...
- 经典算法题每日演练——第十七题 Dijkstra算法
原文:经典算法题每日演练--第十七题 Dijkstra算法 或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划” 这些经典 ...
- 经典算法题每日演练——第八题 AC自动机
原文:经典算法题每日演练--第八题 AC自动机 上一篇我们说了单模式匹配算法KMP,现在我们有需求了,我要检查一篇文章中是否有某些敏感词,这其实就是多模式匹配的问题. 当然你也可以用KMP算法求出,那 ...
- 经典算法题每日演练——第六题 协同推荐SlopeOne 算法
原文:经典算法题每日演练--第六题 协同推荐SlopeOne 算法 相信大家对如下的Category都很熟悉,很多网站都有类似如下的功能,“商品推荐”,"猜你喜欢“,在实体店中我们有导购来为 ...
- 经典算法题每日演练——第七题 KMP算法
原文:经典算法题每日演练--第七题 KMP算法 在大学的时候,应该在数据结构里面都看过kmp算法吧,不知道有多少老师对该算法是一笔带过的,至少我们以前是的, 确实kmp算法还是有点饶人的,如果说红黑树 ...
- CTF---Web入门第十四题 忘记密码了
忘记密码了分值:20 来源: Justatest 难度:中 参与人数:7706人 Get Flag:2232人 答题人数:2386人 解题通过率:94% 找回密码 格式:SimCTF{ } 解题链接: ...
随机推荐
- myEclipse勿删文件怎么恢复
今天码代码的时候项目里有一个jsp文件不小心被删了,又懒得重写,然后发现myEclipse竟然可以恢复被勿删的文件,当然,也仅仅限于最近被删的文件. 具体怎么恢复呢?-------右键点击被删文件所在 ...
- 【C语言的日常实践(十四)】constkeyword详细解释
const是C语言keyword,它定义一个变量不同意变更.使用const在一定程度上,可以提高节目的安全性和可靠性.其他.解const的作用,在看别人的代码时,对理解对方的程序有一定帮助. 1.co ...
- client多线程
1.多线程对象 对象可以是多线程访问,线程可以在这里分为两类: 为完成内部业务逻辑的创建Thread对象,线程需要访问对象. 使用对象的线程外部对象. 进一步假设更精细的划分.业主外螺纹成线等线,. ...
- Flappy bird源代码(略吊)
#include<stdio.h> #include<stdlib.h> #include<conio.h> #include<time.h> #inc ...
- 【数据库摘要】4_Sql_Like
SQL LIKE 操作符 LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式. SQL LIKE 语法 SELECT column_name(s) FROM table_name WHERE ...
- UIStepper使用的具体解释的控制
UIStepper控件类似于UISlider控件,但它有"+"和"-"两个button,单击当中一个可使属性value值递增或递减. 如声音.速度.图片等的大小 ...
- 【原版的】Redis事件驱动内核
Redis事件驱动内核 作者:cf (360电商技术组) 概述 Redis实现了自己的事件驱动,与开源事件库libevent.libev一样,都是基于I/O多路复用技术实现的.出于性能和代码精炼双方面 ...
- Mongodb 之insert瞬时完成,测试数据---飞天博客
这几天看mongdb官方网站,然后将执行数据.突然,我发现,该数据确实很强大,在这里说话数据.我用普通的pc机,amd双核 2.7GHz,4G内存,当然,当系统不只是在测试作为数据库server的.同 ...
- Redis数据结构
Redis数据结构 Redis数据结构详解(一) 前言 Redis和Memcached最大的区别,Redis 除啦支持数据持久化之外,还支持更多的数据类型而不仅仅是简单key-value结构的数据 ...
- 关于“类不能支持Automation操作”错误的解决方法
一段程序IE上老是提示“类不支持Automation操作”的错误,IE6.7.8都一样,但是Firefox可以,后来网上找到如下解决方法: 重新注册下以下文件,问题便解决了:msscript.ocxd ...