CSharp: Prim Algorithms and Kruskal Algorithms

项目结构:

image

 

/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : AggregateRoot.cs

 */
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Common
{
    /// <summary>
    /// DDD 聚合根顶层基类
    /// </summary>
    public abstract class AggregateRoot
    {
        private readonly List<object> _domainEvents = new List<object>();

        public List<object> GetDomainEvents()
        {
            return new List<object>(_domainEvents);
        }

        public void ClearDomainEvents()
        {
            _domainEvents.Clear();
        }
    }
}



/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : Entity.cs

 */

using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Common
{
    /// <summary>
    /// DDD 实体基类:拥有唯一Id标识
    /// </summary>
    public abstract class Entity
    {
        private readonly int _id;

        protected Entity(int id)
        {
            _id = id;
        }

        public int Id => _id;

        public override bool Equals(object obj)
        {
            return obj is Entity entity && Id == entity.Id;
        }

        public override int GetHashCode()
        {
            return Id;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : IValueObject.cs

 */

using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Common
{
    /// <summary>
    /// DDD 值对象:不可变,基于属性判等
    /// </summary>
    public interface IValueObject
    {
        bool Equals(IValueObject other);
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : DomainException.cs

 */
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Common
{
    /// <summary>
    /// 全局领域业务异常
    /// </summary>
    public class DomainException : Exception
    {
        public DomainException(string message) : base($"【领域异常】{message}")
        {
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : UnionFind.cs

 */

using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Common
{
    /// <summary>
    /// 并查集:Kruskal算法依赖,路径压缩
    /// </summary>
    public class UnionFind
    {
        private readonly int[] _parent;

        public UnionFind(int size)
        {
            _parent = new int[size];
            for (int i = 0; i < size; i++)
            {
                _parent[i] = i;
            }
        }

        /// <summary>
        /// 查找根节点+路径压缩
        /// </summary>
        public int Find(int x)
        {
            if (_parent[x] != x)
            {
                _parent[x] = Find(_parent[x]);
            }
            return _parent[x];
        }

        /// <summary>
        /// 合并集合
        /// 返回true:无环合并成功;false:同集合成环
        /// </summary>
        public bool Union(int x, int y)
        {
            int rootX = Find(x);
            int rootY = Find(y);
            if (rootX == rootY)
                return false;
            _parent[rootY] = rootX;
            return true;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : LogisticsNode.cs

 */
using CSharpAlgorithms.PrimKruskal.Common;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Domain.Model
{
    /// <summary>
    /// 物流网点【实体】
    /// 珠宝供应链节点:矿区、加工厂、仓库、线下门店
    /// </summary>
    public class LogisticsNode : Entity
    {
        /// <summary>网点名称</summary>
        public string NodeName { get; }
        /// <summary>网点分类:原料矿区/加工中心/仓储中心/线下门店</summary>
        public string NodeCategory { get; }

        public LogisticsNode(int id, string nodeName, string nodeCategory) : base(id)
        {
            NodeName = nodeName;
            NodeCategory = nodeCategory;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : LogisticsEdge.cs

 */
using CSharpAlgorithms.PrimKruskal.Common;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Domain.Model
{
    /// <summary>
    /// 物流运输线路【值对象】
    /// 权重:综合运输成本(路费、押运、保险、货品损耗),单位:千元
    /// </summary>
    public class LogisticsEdge : IValueObject
    {
        public int StartId { get; }
        public int EndId { get; }
        public double Cost { get; }

        public LogisticsEdge(int startId, int endId, double cost)
        {
            StartId = startId;
            EndId = endId;
            Cost = cost;
        }

        public bool Equals(IValueObject other)
        {
            if (!(other is LogisticsEdge edge))
                return false;
            return StartId == edge.StartId && EndId == edge.EndId && Cost == edge.Cost;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : LogisticsMST.cs

 */
using CSharpAlgorithms.PrimKruskal.Common;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Domain.Model
{
    /// <summary>
    /// 最小生成树【聚合根】
    /// 聚合:所有网点、MST选中线路、全网总成本
    /// </summary>
    public class LogisticsMST : AggregateRoot
    {
        public List<LogisticsNode> AllNodes { get; set; } = new List<LogisticsNode>();
        public List<LogisticsEdge> MstEdges { get; set; } = new List<LogisticsEdge>();
        public double TotalCost { get; set; }

        public void SetNodes(List<LogisticsNode> nodes)
        {
            AllNodes = nodes;
        }

        public void SetResult(List<LogisticsEdge> edges, double totalCost)
        {
            MstEdges = edges;
            TotalCost = totalCost;
        }

        /// <summary>
        /// 格式化线路详情,用于控制台打印输出
        /// </summary>
        public List<Tuple<string, string, double>> GetDetailList()
        {
            Dictionary<int, string> nameMap = new Dictionary<int, string>();
            foreach (var node in AllNodes)
            {
                nameMap[node.Id] = node.NodeName;
            }

            List<Tuple<string, string, double>> list = new List<Tuple<string, string, double>>();
            foreach (var edge in MstEdges)
            {
                list.Add(Tuple.Create(nameMap[edge.StartId], nameMap[edge.EndId], edge.Cost));
            }
            return list;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : PrimAlgorithm.cs

 */
using CSharpAlgorithms.PrimKruskal.Common;
using CSharpAlgorithms.PrimKruskal.Domain.Model;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Domain.Algorithm
{
    /// <summary>
    /// Prim最小生成树 领域算法服务
    /// 适用场景:门店、加工厂密集稠密图
    /// </summary>
    public static class PrimAlgorithm
    {
        public static Tuple<List<LogisticsEdge>, double> Calculate(double[][] adjMatrix, List<LogisticsNode> nodes)
        {
            int nodeCount = nodes.Count;
            if (nodeCount <= 0)
                throw new DomainException("网点集合不能为空,无法生成物流路网");

            double INF = double.MaxValue;
            bool[] inMst = new bool[nodeCount];
            double[] minDist = new double[nodeCount];
            int[] preNode = new int[nodeCount];

            for (int i = 0; i < nodeCount; i++)
            {
                minDist[i] = INF;
                preNode[i] = -1;
            }
            minDist[0] = 0;

            double totalCost = 0;
            List<LogisticsEdge> mstEdges = new List<LogisticsEdge>();

            for (int round = 0; round < nodeCount; round++)
            {
                // 选取距离MST最近节点
                int selectIdx = -1;
                double minVal = INF;
                for (int i = 0; i < nodeCount; i++)
                {
                    if (!inMst[i] && minDist[i] < minVal)
                    {
                        minVal = minDist[i];
                        selectIdx = i;
                    }
                }

                if (selectIdx == -1)
                    throw new DomainException("网点图不连通,无法构建完整物流最小生成树");

                inMst[selectIdx] = true;
                totalCost += minVal;

                // 记录边
                int preIdx = preNode[selectIdx];
                if (preIdx != -1)
                {
                    mstEdges.Add(new LogisticsEdge(preIdx, selectIdx, adjMatrix[preIdx][selectIdx]));
                }

                // 松弛更新邻接点距离
                for (int j = 0; j < nodeCount; j++)
                {
                    double weight = adjMatrix[selectIdx][j];
                    if (!inMst[j] && weight > 0 && weight < minDist[j])
                    {
                        minDist[j] = weight;
                        preNode[j] = selectIdx;
                    }
                }
            }
            return Tuple.Create(mstEdges, totalCost);
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : KruskalAlgorithm.cs

 */
using CSharpAlgorithms.PrimKruskal.Common;
using CSharpAlgorithms.PrimKruskal.Domain.Model;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Domain.Algorithm
{
    /// <summary>
    /// Kruskal最小生成树 领域算法服务
    /// 适用场景:跨城分散网点稀疏图
    /// </summary>
    public static class KruskalAlgorithm
    {
        public static Tuple<List<LogisticsEdge>, double> Calculate(List<LogisticsEdge> edgeList, List<LogisticsNode> nodes)
        {
            int nodeCount = nodes.Count;
            if (nodeCount <= 0)
                throw new DomainException("网点集合不能为空,无法生成物流路网");

            // 边按成本升序排序
            var sortedEdges = edgeList.OrderBy(e => e.Cost).ToList();
            UnionFind uf = new UnionFind(nodeCount);
            List<LogisticsEdge> mstEdges = new List<LogisticsEdge>();
            double totalCost = 0;

            foreach (var edge in sortedEdges)
            {
                if (uf.Union(edge.StartId, edge.EndId))
                {
                    mstEdges.Add(edge);
                    totalCost += edge.Cost;
                    if (mstEdges.Count == nodeCount - 1)
                        break;
                }
            }

            if (mstEdges.Count != nodeCount - 1)
                throw new DomainException("网点图不连通,无法构建完整物流最小生成树");

            return Tuple.Create(mstEdges, totalCost);
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : LogisticsRouteAppService.cs

 */

using CSharpAlgorithms.PrimKruskal.Domain.Algorithm;
using CSharpAlgorithms.PrimKruskal.Domain.Model;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.PrimKruskal.Application
{
    /// <summary>
    /// 物流路线应用服务:只做编排调度,不写核心算法
    /// </summary>
    public class LogisticsRouteAppService
    {
        /// <summary>
        /// Prim生成最小生成树
        /// </summary>
        public LogisticsMST BuildByPrim(double[][] adjMatrix, List<LogisticsNode> nodes)
        {
            var res = PrimAlgorithm.Calculate(adjMatrix, nodes);
            LogisticsMST mst = new LogisticsMST();
            mst.SetNodes(nodes);
            mst.SetResult(res.Item1, res.Item2);
            return mst;
        }

        /// <summary>
        /// Kruskal生成最小生成树
        /// </summary>
        public LogisticsMST BuildByKruskal(List<LogisticsEdge> edges, List<LogisticsNode> nodes)
        {
            var res = KruskalAlgorithm.Calculate(edges, nodes);
            LogisticsMST mst = new LogisticsMST();
            mst.SetNodes(nodes);
            mst.SetResult(res.Item1, res.Item2);
            return mst;
        }
    }
}

  

调用:

/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Prim Algorithms and Kruskal Algorithms 普里姆算法和克鲁斯卡尔算法
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/08/01 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : CSharpDesignPattern
# File      : PrimKruskalBll.cs

 */

using System;
using System.Collections.Generic;
using System.Text;
using CSharpAlgorithms.PrimKruskal.Application;
using CSharpAlgorithms.PrimKruskal.Domain.Model;


namespace CSharpAlgorithms.Bll
{


    /// <summary>
    /// 
    /// </summary>
    public class PrimKruskalBll
    {


        public void Demo()
        {

            // 1、初始化珠宝供应链网点
            List<LogisticsNode> nodeList = new List<LogisticsNode>()
            {
                new LogisticsNode(0,"缅甸翡翠矿区A","原料矿区"),
                new LogisticsNode(1,"云南分拣加工厂","加工中心"),
                new LogisticsNode(2,"深圳总仓储中心","仓储中心"),
                new LogisticsNode(3,"广州旗舰门店","线下门店"),
                new LogisticsNode(4,"上海门店","线下门店"),
                new LogisticsNode(5,"北京门店","线下门店")
            };

            // 2、Prim邻接矩阵,0代表无直达路线
            double[][] adjMatrix = new double[][]
            {
                new double[]{0,12,28,0,0,0},
                new double[]{12,0,8,15,0,0},
                new double[]{28,8,0,6,18,22},
                new double[]{0,15,6,0,25,0},
                new double[]{0,0,18,25,0,14},
                new double[]{0,0,22,0,14,0}
            };

            // 3、Kruskal原始边集合
            List<LogisticsEdge> edgeList = new List<LogisticsEdge>()
            {
                new LogisticsEdge(0,1,12),
                new LogisticsEdge(0,2,28),
                new LogisticsEdge(1,2,8),
                new LogisticsEdge(1,3,15),
                new LogisticsEdge(2,3,6),
                new LogisticsEdge(2,4,18),
                new LogisticsEdge(2,5,22),
                new LogisticsEdge(3,4,25),
                new LogisticsEdge(4,5,14)
            };

            LogisticsRouteAppService appService = new LogisticsRouteAppService();

            // Prim计算输出
            Console.WriteLine("========== Prim算法-稠密网点物流规划 ==========");
            LogisticsMST primMst = appService.BuildByPrim(adjMatrix, nodeList);
            foreach (var item in primMst.GetDetailList())
            {
                Console.WriteLine($"{item.Item1} <--> {item.Item2}  运输成本:{item.Item3:0}千元");
            }
            Console.WriteLine($"全网最低总成本:{primMst.TotalCost:0} 千元\n");

            // Kruskal计算输出
            Console.WriteLine("========== Kruskal算法-稀疏跨城网点规划 ==========");
            LogisticsMST krusMst = appService.BuildByKruskal(edgeList, nodeList);
            foreach (var item in krusMst.GetDetailList())
            {
                Console.WriteLine($"{item.Item1} <--> {item.Item2}  运输成本:{item.Item3:0}千元");
            }
            Console.WriteLine($"全网最低总成本:{krusMst.TotalCost:0} 千元");

            Console.ReadKey();

        }

    }
}

  

输出:

0c757497-b6c8-4bab-93f0-959f54b5fe91

 

posted @ 2026-08-08 06:43  ®Geovin Du Dream Park™  阅读(3)  评论(0)    收藏  举报