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)}");
            });
        }
    }
}

运行如图:

相关推荐
秋91 天前
AI快速生成可编辑的流程图的方法
流程图
宁静致远20211 天前
Mermaid VSCode插件制作流程图保存方法
ide·vscode·流程图
min1811234563 天前
小型网站开发简易流程步骤 在线画图工具快速生成开发流程图方法
论文阅读·信息可视化·毕业设计·流程图·论文笔记
DYuW5gBmH4 天前
Kafka 成功消费消息的完整流程图
分布式·kafka·流程图
数说星榆1816 天前
无人员伤亡车辆事故处理流程图 快速结案流程
架构·电脑·流程图·职场发展·课程设计
dajun1811234566 天前
信息系统运维管理全流程详解 在线画图工具绘制运维流程图表技巧
运维·数据库·信息可视化·流程图·旅游·论文笔记
数说星榆1816 天前
罕见病例报告流程图学术论文用
论文阅读·人工智能·流程图
fish-man7 天前
测试流程图显示
流程图
橙色日落7 天前
Vue2 + LogicFlow 实现可视化流程图编辑功能+常用属性大全
前端·vue·流程图·logicflow
数说星榆1818 天前
油气勘探数据可视化流程图制作
论文阅读·信息可视化·毕业设计·流程图·论文笔记·毕设