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

发布于:2025-06-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

上一篇流程图示例

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

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

最短路径算法-CSDN博客

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

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

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

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

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

如下图:

新建窗体FormDijkstraDiagram,

窗体FormDijkstraDiagram设计器程序如下:

文件:FormDijkstraDiagram.designer.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

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

运行如图: