C#使用MindFusion.Diagramming框架绘制流程图(3):加权图的最短路径算法

上一篇流程图示例

C#使用MindFusion.Diagramming框架绘制流程图(2):流程图示例-CSDN博客

这里我们以之前的最短路径算法为例

最短路径算法-CSDN博客

使用MindFusion.Diagramming画出流程图,并按照当前示例图实现最短路径算法

[迪杰斯特拉最短路径算法]

我们使用MindFusion.Diagramming.DiagramItem的属性Weight权重来进行处理

我们设定MindFusion.Diagramming.DiagramLink连接是双向的权重,连接的文本就显示为权重Weight

MindFusion.Diagramming.DiagramNode节点之间的最短路径

如下图:

新建窗体FormDijkstraDiagram,

窗体FormDijkstraDiagram设计器程序如下:

文件:FormDijkstraDiagram.designer.cs

cs 复制代码
namespace FlowDiagramDemo
{
    partial class FormDijkstraDiagram
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.diagramView1 = new MindFusion.Diagramming.WinForms.DiagramView();
            this.diagram1 = new MindFusion.Diagramming.Diagram();
            this.btnGetMinRouter = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // diagramView1
            // 
            this.diagramView1.Diagram = this.diagram1;
            this.diagramView1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.diagramView1.LicenseKey = null;
            this.diagramView1.Location = new System.Drawing.Point(0, 0);
            this.diagramView1.Name = "diagramView1";
            this.diagramView1.Size = new System.Drawing.Size(989, 679);
            this.diagramView1.TabIndex = 3;
            this.diagramView1.Text = "diagramView1";
            // 
            // diagram1
            // 
            this.diagram1.TouchHitDistance = null;
            // 
            // btnGetMinRouter
            // 
            this.btnGetMinRouter.Location = new System.Drawing.Point(14, 2);
            this.btnGetMinRouter.Name = "btnGetMinRouter";
            this.btnGetMinRouter.Size = new System.Drawing.Size(223, 37);
            this.btnGetMinRouter.TabIndex = 4;
            this.btnGetMinRouter.Text = "打印节点A到达其他节点的最短路径";
            this.btnGetMinRouter.UseVisualStyleBackColor = true;
            this.btnGetMinRouter.Click += new System.EventHandler(this.btnGetMinRouter_Click);
            // 
            // FormDijkstraDiagram
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(989, 679);
            this.Controls.Add(this.btnGetMinRouter);
            this.Controls.Add(this.diagramView1);
            this.Name = "FormDijkstraDiagram";
            this.Text = "加权图(Graph)的迪杰斯特拉最短路径算法-斯内科";
            this.Load += new System.EventHandler(this.FormDijkstraDiagram_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private MindFusion.Diagramming.WinForms.DiagramView diagramView1;
        private MindFusion.Diagramming.Diagram diagram1;
        private System.Windows.Forms.Button btnGetMinRouter;
    }
}

窗体FormDijkstraDiagram相关程序如下:

文件:FormDijkstraDiagram.cs

cs 复制代码
using MindFusion.Diagramming;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FlowDiagramDemo
{
    public partial class FormDijkstraDiagram : Form
    {
        /// <summary>
        /// 顶点个数
        /// </summary>
        public const int VertexCount = 6;
        /// <summary>
        /// 无法直接到达的距离,无限远的距离
        /// </summary>
        public const float Infinity = 99999;
        /// <summary>
        /// 图的顶点集合,数组的每一个元素代表一个顶点【元组的第一项代表顶点名称,第二项代表是否已被访问】
        /// </summary>
        public Tuple<string, bool>[] VertexTuples = new Tuple<string, bool>[VertexCount];
        /// <summary>
        /// 表示两点之间的距离。如:
        /// MatrixDistance[0,1]=88 表示 【索引为0的顶点】 到 【索引为1的顶点】 之间的距离是88
        /// </summary>
        public float[,] MatrixDistance = new float[VertexCount, VertexCount];
        /// <summary>
        /// 起点到终点的最短距离
        /// 元组的第一项代表起点名称,第二项代表终点名称,第三项代表最短距离值,第四项代表当前最短距离的上一个顶点索引
        /// 本文起点名称固定设置为A,获取A到其他任何一个顶点之间的最短距离,以及运行轨迹路线
        /// </summary>
        public Tuple<string, string, float, int>[] MinDistanceTuples = new Tuple<string, string, float, int>[VertexCount];
        int sequence = 0;
        public FormDijkstraDiagram()
        {
            InitializeComponent();
            diagram1.AllowSelfLoops = false;//不允许自旋链接
            diagram1.ShowGrid = true;//显示网格
            diagram1.AllowUnconnectedLinks = false;//不允许用户绘制未连接到任何节点的链接
            diagram1.NodeCreating += Diagram1_NodeCreating;//不允许创建节点
            diagram1.LinkCreating += Diagram1_LinkCreating;//不允许创建连接
            diagram1.NodeDeleting += Diagram1_NodeDeleting;//不允许删除节点
            diagram1.LinkDeleting += Diagram1_LinkDeleting;//不允许删除连接
        }

        private void Diagram1_LinkDeleting(object sender, LinkValidationEventArgs e)
        {
            e.Cancel = true;
        }

        private void Diagram1_NodeDeleting(object sender, NodeValidationEventArgs e)
        {
            e.Cancel = true;
        }

        private void Diagram1_LinkCreating(object sender, LinkValidationEventArgs e)
        {
            e.Cancel = true;
        }

        private void Diagram1_NodeCreating(object sender, NodeValidationEventArgs e)
        {
            e.Cancel = true;
        }

        /// <summary>
        /// 初始化所有节点
        /// </summary>
        private void InitNode()
        {
            sequence = 0;//复位序列号
            char label = 'A';
            for (int i = 0; i < VertexTuples.Length; i++)
            {
                VertexTuples[i] = Tuple.Create(((char)(label + i)).ToString(), false);

                ShapeNode diagramNode = new ShapeNode(diagram1);
                diagramNode.AllowIncomingLinks = true;
                diagramNode.AllowOutgoingLinks = true;
                diagramNode.Text = VertexTuples[i].Item1;
                diagramNode.ToolTip = VertexTuples[i].Item1;
                diagramNode.Font = new Font("宋体", 14);
                sequence++;
                diagramNode.Id = sequence;
                diagramNode.Tag = i;
                float x = 100;//i==0时,起点A坐标
                float y = 10;//i==0时,起点A坐标
                if (i == 1)
                {
                    x = 40;
                    y = 50;
                }
                else if (i == 2)
                {
                    x = 90;
                    y = 70;
                }
                else if (i == 3)
                {
                    x = 170;
                    y = 60;
                }
                else if (i == 4)
                {
                    x = 70;
                    y = 130;
                }
                else if (i == 5)
                {
                    x = 150;
                    y = 155;
                }
                diagramNode.Bounds = new RectangleF(x, y, 15, 15);
                diagramNode.Shape = Shapes.Ellipse;//形状:圆
                diagram1.Nodes.Add(diagramNode);                
            }
        }

        /// <summary>
        /// 添加一条无向边【认为距离是无向的 即A→B,同时B→A】
        /// 对图来说,就是增加两条连线←→【正向连线 与 反向连线】
        /// </summary>
        /// <param name="startIndex">起始索引</param>
        /// <param name="endIndex">到达索引</param>
        /// <param name="distance">两点之间的距离</param>
        private void AddLink(int startIndex, int endIndex, float distance)
        {
            MatrixDistance[startIndex, endIndex] = distance;
            //如果距离是有方向的,则去掉下面一行代码
            MatrixDistance[endIndex, startIndex] = distance;
            DiagramLink diagramLink = new DiagramLink(diagram1, diagram1.Nodes[startIndex], diagram1.Nodes[endIndex]);
            diagramLink.Weight = distance;//设置连接的权重
            diagramLink.Font = new Font("宋体", 14);
            diagramLink.Text = distance.ToString();
            if (diagram1.Links.Any(x => x.Origin == diagram1.Nodes[endIndex] && x.Destination == diagram1.Nodes[startIndex]))
            {
                //如果链接是双向的即←→,则设置文本为空
                diagramLink.Text = "";
            }
            sequence++;
            diagramLink.Id = sequence;
            diagramLink.Tag = $"【{diagram1.Nodes[startIndex].Text}】->【{diagram1.Nodes[endIndex].Text}】,权重Weight为【{distance}】";
            diagram1.Links.Add(diagramLink);

            //增加反向连线,权重Weight与正向连线一致
            DiagramLink reverseLink = new DiagramLink(diagram1, diagram1.Nodes[endIndex], diagram1.Nodes[startIndex]);
            reverseLink.Weight = distance;//设置连接的权重
            reverseLink.Font = new Font("宋体", 14);
            reverseLink.Text = distance.ToString();
            if (diagram1.Links.Any(x => x.Origin == diagram1.Nodes[startIndex] && x.Destination == diagram1.Nodes[endIndex]))
            {
                //如果链接是双向的即←→,则设置文本为空
                reverseLink.Text = "";
            }
            sequence++;
            reverseLink.Id = sequence;
            reverseLink.Tag = $"【{diagram1.Nodes[startIndex].Text}】->【{diagram1.Nodes[startIndex].Text}】,权重Weight为【{distance}】";
            diagram1.Links.Add(reverseLink);
        }

        /// <summary>
        /// 初始化【权重图】或【距离图】:
        /// 一、初始化所有顶点,并标记所有顶点都是未访问的
        /// 二、初始化任意两点的距离都是无穷大的
        /// 三、图的实际距离【两点之间可以直接连接的部分,也就是 直接连接的顶点之间的距离 覆盖原来的默认值无穷大】
        /// </summary>
        public void InitGraph()
        {
            InitNode();
            //初始化最短距离为无穷大,所有矩阵距离都为无穷大
            for (int i = 0; i < VertexCount; i++)
            {
                for (int j = 0; j < VertexCount; j++)
                {
                    MatrixDistance[i, j] = Infinity;
                }
            }

            #region 添加边Edge:图的连线部分
            //AB AC AD
            AddLink(0, 1, 5);
            AddLink(0, 2, 3);
            AddLink(0, 3, 19);
            //BC BE
            AddLink(1, 2, 10);
            AddLink(1, 4, 8);
            //CD CE CF
            AddLink(2, 3, 14);
            AddLink(2, 4, 12);
            AddLink(2, 5, 9);
            //DE DF
            AddLink(3, 4, 5);
            AddLink(3, 5, 4);
            //EF
            AddLink(4, 5, 4);
            #endregion

            #region 默认最短距离为无穷大
            for (int i = 0; i < VertexCount; i++)
            {
                MinDistanceTuples[i] = Tuple.Create(VertexTuples[0].Item1, VertexTuples[i].Item1, Infinity, 0);
            }
            #endregion
        }

        /// <summary>
        /// 设置第一个顶点A到其他顶点的距离是直连的
        /// ①如果当前顶点A到其他顶点的距离小于Infinity,就以当前距离作为初始最小的距离
        /// ②找出未访问的顶点中,距离最小的顶点的顶点,然后标记顶点为已访问的
        /// ③如果当前节点未访问,并且 最短距离+【最短距离的顶点可到达的顶点的距离之和】,那么就设置当前路径为最小距离 需要一个双重循环
        /// </summary>
        public void GetRouterPath()
        {
            int startIndex = 0;
            //将第一个顶点更新为已访问
            VertexTuples[startIndex] = Tuple.Create(VertexTuples[startIndex].Item1, true);
            for (int i = 0; i < VertexCount; i++)
            {
                if (MatrixDistance[startIndex, i] < MinDistanceTuples[i].Item3)
                {
                    //因元组无法为第三项赋值,只能重建元组了
                    MinDistanceTuples[i] = Tuple.Create(MinDistanceTuples[i].Item1, MinDistanceTuples[i].Item2, MatrixDistance[startIndex, i], startIndex);
                }
            }
            for (int cnt = 1; cnt < VertexCount; cnt++)
            {
                //找出最小的距离对应的顶点索引,并将当前顶点设置为已访问
                int minIndex = FindMinDistanceIndex();
                float minDist = MinDistanceTuples[minIndex].Item3;
                //设置为已访问该顶点
                VertexTuples[minIndex] = Tuple.Create(VertexTuples[minIndex].Item1, true);
                for (int i = 0; i < VertexCount; i++)
                {
                    //如果当前节点未访问,并且 最短距离+【最短距离的顶点可到达的顶点的距离之和】 还要比原来的还要短,就用新的最短距离赋值
                    if (!VertexTuples[i].Item2 && minDist + MatrixDistance[minIndex, i] < MinDistanceTuples[i].Item3)
                    {
                        //记录上一个最短距离顶点的索引
                        MinDistanceTuples[i] = Tuple.Create(MinDistanceTuples[i].Item1, MinDistanceTuples[i].Item2, minDist + MatrixDistance[minIndex, i], minIndex);
                    }
                }
            }
        }

        /// <summary>
        /// 显示最短路径的详细信息
        /// </summary>
        /// <param name="vertexIndex"></param>
        /// <returns></returns>
        public string DisplayDescription(int vertexIndex)
        {
            return $"最短路径长度:{VertexTuples[0].Item1}->{VertexTuples[vertexIndex].Item1},最短距离{MinDistanceTuples[vertexIndex].Item3},上一个顶点索引:{MinDistanceTuples[vertexIndex].Item4}.\n" +
                $"  完整路由:{GetSerialPath(vertexIndex)}";
        }

        /// <summary>
        /// 获取连续节点路线
        /// </summary>
        /// <param name="parentIndex"></param>
        /// <returns></returns>
        private string GetSerialPath(int parentIndex)
        {
            Stack<string> stack = new Stack<string>();
            stack.Push(VertexTuples[parentIndex].Item1);
            while (parentIndex != 0)
            {
                parentIndex = MinDistanceTuples[parentIndex].Item4;
                stack.Push(VertexTuples[parentIndex].Item1);
            }
            return string.Join("->", stack);
        }

        /// <summary>
        /// 找出最小的距离对应的顶点索引
        /// </summary>
        /// <returns></returns>
        private int FindMinDistanceIndex()
        {
            //找出未访问的顶点中,距离最小的顶点的起点索引
            int minIndex = -1;//最小距离所在的索引
            float minDist = Infinity;//最小距离
            for (int i = 0; i < VertexCount; i++)
            {
                if (!VertexTuples[i].Item2 && MinDistanceTuples[i].Item3 < minDist)
                {
                    minIndex = i;
                    minDist = MinDistanceTuples[i].Item3;
                }
            }
            return minIndex;
        }

        private void FormDijkstraDiagram_Load(object sender, EventArgs e)
        {
            InitGraph();
        }

        private async void btnGetMinRouter_Click(object sender, EventArgs e)
        {
            await Task.Run(() => 
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                //这里最短路径算法
                GetRouterPath();
                List<string> pathRouterList = new List<string>();
                for (int vertexIndex = 0; vertexIndex < VertexCount; vertexIndex++)
                {
                    pathRouterList.Add(DisplayDescription(vertexIndex));
                }
                stopwatch.Stop();
                MessageBox.Show($"获取最短路径耗时:【{stopwatch.Elapsed.TotalMilliseconds}】ms", "运行耗时(ms)");
                MessageBox.Show($"{string.Join("\n\n", pathRouterList)}");
            });
        }
    }
}

运行如图:

相关推荐
檐下翻书1737 天前
公司组织架构调整工具 在线可视化编辑平台
论文阅读·人工智能·信息可视化·架构·去中心化·流程图
程序员JerrySUN8 天前
别再把 HTTPS 和 OTA 看成两回事:一篇讲透 HTTPS 协议、安全通信机制与 Mender 升级加密链路的完整文章
android·java·开发语言·深度学习·流程图
GOWIN革文品牌咨询8 天前
工业软件越专业越难卖?用“表达可读化”把架构图、流程图、数据图讲清
流程图
min1811234568 天前
PC流程图模板大全 中文定制化满足不同行业需求
架构·pdf·流程图
min1811234568 天前
组织结构图导出PDF 高清无水印在线生成
网络·人工智能·架构·pdf·流程图·copilot
檐下翻书1739 天前
企业组织架构图导出Word 在线编辑免费工具
人工智能·信息可视化·去中心化·word·流程图·ai编程
檐下翻书1739 天前
PC流程图模板大全 中文定制化满足不同行业需求
论文阅读·信息可视化·数据挖掘·数据分析·流程图
xiami_world9 天前
深度评测:5款AI流程图生成工具——图像识别、Mermaid支持与文档解析能力对比
人工智能·ai·信息可视化·ai作画·流程图
a1117769 天前
堆叠式流程图编辑器(html 开源)
开发语言·前端·javascript·开源·编辑器·html·流程图