克鲁斯卡尔(Kruskal)算法(代码)
算法代码
C#代码
using System;
using System.Linq;
namespace Kruskal
{
class Program
{
static void Main(string[] args)
{
Edge[] edges = new Edge[] {
new Edge(){Begin = 4, End = 7, Weight = 7 },
new Edge(){Begin = 2, End = 8, Weight = 8 },
new Edge(){Begin = 0, End = 1, Weight = 10 },
new Edge(){Begin = 0, End = 5, Weight = 11 },
new Edge(){Begin = 1, End = 8, Weight = 12 },
new Edge(){Begin = 3, End = 7, Weight = 16 },
new Edge(){Begin = 1, End = 6, Weight = 16 },
new Edge(){Begin = 5, End = 6, Weight = 17 },
new Edge(){Begin = 1, End = 2, Weight = 18 },
new Edge(){Begin = 6, End = 7, Weight = 19 },
new Edge(){Begin = 3, End = 4, Weight = 20 },
new Edge(){Begin = 3, End = 8, Weight = 21 },
new Edge(){Begin = 2, End = 3, Weight = 22 },
new Edge(){Begin = 3, End = 6, Weight = 24 },
new Edge(){Begin = 4, End = 5, Weight = 26 },
};
int numberOfVertex = 9;
Kruskal(edges, numberOfVertex);
}
static void Kruskal(Edge[] edges, int numberOfVertex)
{
bool isDemonstrate = false; // (非必要代码)
int[] vertex = new int[numberOfVertex]; // (非必要代码)T连通图的起始顶点。
int[] parent = new int[numberOfVertex]; // 若连通图中存在环,那么从形成环的这条边的
// 两个顶点的任意顶点出发,都能沿着parent
// 数组找到相同的尾顶点下标。parent数组实际
// 存储着一个或多个或多个连通图。
if (isDemonstrate) // (非必要代码)
{
for (int i = 0; i < numberOfVertex; i++)
{
vertex[i] = i;
}
}
for (int i = 0; i < numberOfVertex; i++) // 初始化路径的各尾顶点下标。
{
parent[i] = 0;
}
edges.OrderBy(e => e.Weight); // 按权值的升序对边集进行排序。
/** 从边集中逐个取出边,去测试这条边是否会构
成环,不能构成环则将边的尾顶点下标加入
parent数组中。*/
for (int i = 0; i < edges.Length; i++)
{
Edge edge = edges[i];
int n = Find(parent, edge.Begin),
m = Find(parent, edge.End);
if (n != m)
{
/** 若n与m不等,则此边未与现有生成树形成环路。
于是,将边的尾顶点下标放入数组的下标为边的
头顶点的parent数组中。表示现在该尾顶点已经
在生成树的集合中。*/
parent[n] = m; // 将边的尾顶点下标放入数组parent。(两者任选其一)
//parent[m] = n; // 将边的头顶点下标放入数组parent。(两者任选其一)
string result = $"({edge.Begin}, {edge.End}) = {edge.Weight}";
Console.WriteLine(result); // 输出边。
if (isDemonstrate) // (非必要代码)
{
Console.Write("非连通图头顶点下标vertex:");
PrintArray(vertex);
Console.Write("非连通图尾顶点下标parent:"); // 查看parent数组。
PrintArray(parent);
}
}
}
}
static int Find(int[] parent, int vertex)
{
while (parent[vertex] > 0)
{
vertex = parent[vertex]; // 寻找路径中下个顶点的下标。
}
return vertex;
}
static void PrintArray(int[] array)
{
Console.Write("[ ");
for (int i = 0; i < array.Length - 1; i++)
{ // 输出数组的前面n-1个
Console.Write($"{ToInfinity(array[i])}, ");
}
if (array.Length > 0) // 输出数组的最后1个
{
int n = array.Length - 1;
Console.Write($"{ToInfinity(array[n])}");
}
Console.WriteLine(" ]");
}
static string ToInfinity(int i) => i == int.MaxValue ? "∞" : i.ToString();
}
class Edge
{
public int Begin { get; set; }
public int End { get; set; }
public int Weight { get; set; }
}
}
/**
(4,7) = 7
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 0, 0, 0, 0, 7, 0, 0, 0, 0 ]
(2,8) = 8
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 0, 0, 8, 0, 7, 0, 0, 0, 0 ]
(0,1) = 10
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 0, 8, 0, 7, 0, 0, 0, 0 ]
(0,5) = 11
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 0, 7, 0, 0, 0, 0 ]
(1,8) = 12
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 0, 7, 8, 0, 0, 0 ]
(3,7) = 16
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 7, 7, 8, 0, 0, 0 ]
(1,6) = 16
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 7, 7, 8, 0, 0, 6 ]
(6,7) = 19
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 7, 7, 8, 7, 0, 6 ]
*/
对上面C#代码的补充
如果需要模拟前面文章中在连通图中寻找顶点的办法,那么可以将下面的代码添加到进来:
static void Kruskal2(Edge[] edges, int numberOfVertex)
{
var sets = new List<VertexSet>(); // 用于存放各连通分量的列表。
// 连通分量中顶点被放在一个顶点集合中。
for (int i = 0; i < numberOfVertex; i++) // 初始时,各顶点自成一个连通分量(顶点集合)。
{
sets.Add(new VertexSet(i));
}
edges.OrderBy(e => e.Weight); // 按权值的升序对边集进行排序。
/** 从边集中逐个取出边,去测试这条边是否会构
成环,不能构成环则将分别包含边e的两个顶点
的量连通分量(顶点集合)合并为tmp,然后从
连通分量列表中删除这两个连通分量,并将新合
成的连通分量tmp加入列表。*/
for (int i = 0; i < edges.Length; i++)
{
Edge edge = edges[i];
VertexSet n = Search(sets, edge.Begin),
m = Search(sets, edge.End);
if (n != m)
{
var tmp = n.Concat(m);
sets.Remove(n);
sets.Remove(m);
sets.Add(tmp);
string result = $"({edge.Begin}, {edge.End}) = {edge.Weight}";
Console.WriteLine(result); // 输出边。
}
}
//Console.WriteLine($"Number of Vertex Set: {sets.Count}");
}
static VertexSet Search(IList<VertexSet> s, int code)
{
return s.First(e => e.Has(code));
}
class Vertex
{
public int Index { get; set; }
}
class VertexSet
{
public VertexSet() { }
public VertexSet(int index)
{
Add(new Vertex() { Index = index });
}
public HashSet<Vertex> Vertexes { get; } = new HashSet<Vertex>();
public Vertex Add(Vertex v)
{
Vertexes.Add(v);
return v;
}
public Vertex Remove(Vertex v)
{
if (Has(v))
{
return Vertexes.Remove(v) ? v : null;
}
return null;
}
public bool Has(Vertex v) => Has(v.Index);
public bool Has(int index) => Vertexes.Any(e => e.Index == index);
public VertexSet Concat(VertexSet second)
{
// 将当前顶点集合中的顶点和需要拼接的顶点集合中的顶点放入一个新的顶点集合vs中,
// 并返回该新的顶点集合vs。
VertexSet vs = new VertexSet();
for (int i = 0; i < Vertexes.Count; i++)
{
vs.Add(Vertexes.ElementAt(i));
}
for (int i = 0; i < second.Vertexes.Count; i++)
{
vs.Add(second.Vertexes.ElementAt(i));
}
return vs;
}
}
TypeScript代码
class Edge {
Begin: number;
End: number;
Weight: number;
constructor(begin: number, end: number, weight: number) {
this.Begin = begin;
this.End = end;
this.Weight = weight;
}
}
function kruskal(edges: Edge[], numberOfVertex: number) {
let isDemonstrate: boolean = true; // (非必要代码)
let vertex: number[] = []; // (非必要代码)T连通图的起始顶点。
let parent: number[] = []; /** 若连通图中存在环,那么从形成环的这条边的
两个顶点的任意顶点出发,都能沿着parent
数组找到相同的尾顶点下标。parent数组实际
存储着一个或多个或多个连通图。*/
if (isDemonstrate) // (非必要代码)
{
for (let i = 0; i < numberOfVertex; i++) {
vertex[i] = i;
}
}
for (let i = 0; i < numberOfVertex; i++) // 初始化路径的各尾顶点下标。
{
parent[i] = 0;
}
edges.sort(e => e.Weight); // 按权值的升序对边集进行排序。
/** 从边集中逐个取出边,去测试这条边是否会构
成环,不能构成环则将边的尾顶点下标加入
parent数组中。*/
for (let i = 0; i < edges.length; i++) {
let edge: Edge = edges[i];
let n: number = Find(parent, edge.Begin),
m: number = Find(parent, edge.End);
if (n != m) {
/** 若n与m不等,则此边未与现有生成树形成环路。
于是,将边的尾顶点下标放入数组的下标为边的
头顶点的parent数组中。表示现在该尾顶点已经
在生成树的集合中。*/
parent[n] = m; // 将边的尾顶点下标放入数组parent。(两者任选其一)
//parent[m] = n; // 将边的头顶点下标放入数组parent。(两者任选其一)
let result: string = `(${edge.Begin}, ${edge.End}) = ${edge.Weight}`;
console.log(result); // 输出边。
if (isDemonstrate) // (非必要代码)
{
console.log(`非连通图头顶点下标vertex:${printArray(vertex)}`);
// 查看parent数组。
console.log(`非连通图尾顶点下标parent:${printArray(parent)}`);
}
}
}
}
function Find(parent: number[], vertex: number): number {
while (parent[vertex] > 0) {
vertex = parent[vertex]; // 寻找路径中下个顶点的下标。
}
return vertex;
}
function printArray(array: number[]): string {
let str: string[] = [];
str.push("[ ");
for (let i = 0; i < array.length - 1; i++) // 输出数组的前n-1个
{
str.push(`${toInfinity(array[i])}, `)
}
if (array.length > 0) // 输出数组的最后1个
{
let n: number = array.length - 1;
str.push(`${toInfinity(array[n])}`);
}
str.push(" ]");
return str.join("");
}
function toInfinity(i: number) {
return i == Number.MAX_VALUE ? "∞" : i.toString();
}
function Main() {
let edges: Edge[] = [
new Edge(4, 7, 7),
new Edge(2, 8, 8),
new Edge(0, 1, 10),
new Edge(0, 5, 11),
new Edge(1, 8, 12),
new Edge(3, 7, 16),
new Edge(1, 6, 16),
new Edge(5, 6, 17),
new Edge(1, 2, 18),
new Edge(6, 7, 19),
new Edge(3, 4, 20),
new Edge(3, 8, 21),
new Edge(2, 3, 22),
new Edge(3, 6, 24),
new Edge(4, 5, 26),
];
let numberOfVertex: number = 9;
kruskal(edges, numberOfVertex);
}
Main();
/**
运行结果:
(4, 7) = 7
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 0, 0, 0, 0, 7, 0, 0, 0, 0 ]
(2, 8) = 8
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 0, 0, 8, 0, 7, 0, 0, 0, 0 ]
(0, 1) = 10
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 0, 8, 0, 7, 0, 0, 0, 0 ]
(0, 5) = 11
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 0, 7, 0, 0, 0, 0 ]
(1, 8) = 12
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 0, 7, 8, 0, 0, 0 ]
(3, 7) = 16
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 7, 7, 8, 0, 0, 0 ]
(1, 6) = 16
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 7, 7, 8, 0, 0, 6 ]
(6, 7) = 19
非连通图头顶点下标:[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
非连通图尾顶点下标:[ 1, 5, 8, 7, 7, 8, 7, 0, 6 ]
*/
参考资料:
《大话数据结构》 - 程杰 著 - 清华大学出版社 第252页
克鲁斯卡尔(Kruskal)算法(代码)的更多相关文章
- 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用
图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...
- 洛谷P3366【模板】最小生成树-克鲁斯卡尔Kruskal算法详解附赠习题
链接 题目描述 如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz 输入输出格式 输入格式: 第一行包含两个整数N.M,表示该图共有N个结点和M条无向边.(N<=5000,M&l ...
- 图解最小生成树 - 克鲁斯卡尔(Kruskal)算法
我们在前面讲过的<克里姆算法>是以某个顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树的.同样的思路,我们也可以直接就以边为目标去构建,因为权值为边上,直接找最小权值的边来构建生成树 ...
- 克鲁斯卡尔(Kruskal)算法
# include <stdio.h> # define MAX_VERTEXES //最大顶点数 # define MAXEDGE //边集数组最大值 # define INFINITY ...
- 克鲁斯卡尔(Kruskal)算法求最小生成树
/* *Kruskal算法求MST */ #include <iostream> #include <cstdio> #include <cstring> #inc ...
- 最小生成树——Kruskal(克鲁斯卡尔)算法
[0]README 0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在 理解 Kruskal(克鲁斯卡尔)算法 的idea 并用 源代码加以实现: 0.2)最小生成树的基础知识,参见 ...
- 经典问题----最小生成树(kruskal克鲁斯卡尔贪心算法)
题目简述:假如有一个无向连通图,有n个顶点,有许多(带有权值即长度)边,让你用在其中选n-1条边把这n个顶点连起来,不漏掉任何一个点,然后这n-1条边的权值总和最小,就是最小生成树了,注意,不可绕成圈 ...
- 最小生成树之克鲁斯卡尔(kruskal)算法
#include <iostream> #include <string> using namespace std; typedef struct MGraph{ string ...
- 数据结构与算法——克鲁斯卡尔(Kruskal)算法
目录 应用场景-公交站问题 克鲁斯卡尔算法介绍 克鲁斯卡尔算法图解 克鲁斯卡尔算法分析 如何判断回路? 代码实现 无向图构建 克鲁斯卡尔算法实现 获取一个点的终点解释 应用场景-公交站问题 某城市新增 ...
- 图->连通性->最小生成树(克鲁斯卡尔算法)
文字描述 上一篇博客介绍了最小生成树(普里姆算法),知道了普里姆算法求最小生成树的时间复杂度为n^2, 就是说复杂度与顶点数无关,而与弧的数量没有关系: 而用克鲁斯卡尔(Kruskal)算法求最小生成 ...
随机推荐
- C# yield return 原理探究
天需要些一个小工具,需要使用到多线程读写程序集,接口方法返回值类型需要为"IEnumerable<string>"这里用到了"yield return&quo ...
- Detach blobs with a contact point
https://answers.opencv.org/question/87583/detach-blobs-with-a-contact-point/ 一.问题描述 带有接触点的斑点时遇到问题,需要 ...
- Android 之 TableLayout 布局详解
TableLayout简介 •简介 Tablelayout 类以行和列的形式对控件进行管理,每一行为一个 TableRow 对象,或一个 View 控件. 当为 TableRow 对象时,可在 Tab ...
- 【死磕JVM】给同事讲了一遍GC后,他要去面试,年轻人,就是容易冲动!
前言 在一个风和日丽的中午,和同事小勇一起走在公司楼下的小公园里面,看到很多的小姐姐,心想什么时候能够和这些小姐姐一起讨论人生呀,美滋滋,嘿嘿嘿. 收起你的哈喇子好不好,小勇总是在这个时候发出声音,挺 ...
- Linux常用命令-基础部分
Linux介绍 Linux是一款开源的操作系统,免费,开源,安全,高效,处理高并发非常强悍,很多企业级开发项目都部署在Linux/UNIX上. 创始人:Linus Torvalds 林纳斯 Linux ...
- 翻译:《实用的Python编程》07_05_Decorated_methods
目录 | 上一节 (7.4 装饰器) | 下一节 (8 测试和调试) 7.5 装饰方法 本节讨论一些与方法定义结合使用的内置装饰器. 预定义的装饰器 在类定义中,有许多预定义的装饰器用于指定特殊类型的 ...
- [Fundamental of Power Electronics]-PART I-4.开关实现-4.2 功率半导体器件概述
4.2 功率半导体器件概述 功率半导体设计中最根本的挑战是获得高击穿电压,同时保持低正向压降和导通电阻.一个密切相关的问题是高压低导通电阻器件的开关时间更长.击穿电压,导通电阻和开关时间之间的折衷是各 ...
- 【Flutter 3-5】Flutter进阶教程——在Flutter中使用Lottie动画
作者 | 弗拉德 来源 | 弗拉德(公众号:fulade_me) Lottie动画 在移动开发中总是需要展示一些动画特效,作为程序员的我们并不是很擅长用代码做动画,即便是有些动画可以实现,在跨平台的过 ...
- (十)VMware Harbor 日志管理
VMware Harbor 日志管理 1. 项目日志 每个项目下都有一个"日志"页签. 单击"日志"可以列出所有日志.可以按用户名或"高级搜索&quo ...
- JDK8接口新关键字default和static
JDK8及以后,允许我们在接口中定义static方法和default方法. public interface InterfaceDemo { // static修饰符定义静态方法 static voi ...