Назад Вперед Зміст

Алгоритм Дейкстри

Задача про найкоротші шляхи з заданої вершини до всіх інших (single-source shortest path problem).

Алгоритм застосовується лише для графів без ребер від'ємної ваги та петель.

Процес релаксації ребра

Приклад

Дано граф:

Знайти найкоротший шлях з вершини 1 до вершини 5.

Позначення:

H - множина відвіданих вершин

D[i] - поточне найкоротше відоме відстань з вершини s до вершини j

Prev[i] - номер вершини, що передує i у шляху

Ініціалізація:

Встановлюємо D[i] = ∞ для всіх вершин, крім початкової

D[s] = 0

Поміщаємо всі вершини у чергу з пріоритетом Q (min-heap), де пріоритет вершини i - значення D[i]

Алгоритм:

  1. Витягаємо з черги Q вершину v з мінімальним пріоритетом (найближчу до S)
  2. Позначаємо вершину v як відвідану (додаємо v до множини H)
  3. Для кожної вершини u, суміжної з v і не включеної до H:

Візуалізація процесу:

Відновлення найкоротшого шляху:

Кроки алгоритму:

  1. Створюємо масив прапорців відвіданих вершин та масив відстаней
  2. Ініціалізуємо всі відстані як ∞, а всі вершини як невідвідані
  3. Встановлюємо відстань від s до s = 0
  4. Для кожної вершини знаходимо найкоротший шлях через сусідні вершини
  5. Якщо відстань до цільової вершини залишилась ∞ - шляху не існує
  6. Відновлюємо шлях, проходячи від кінцевої вершини до початкової

РЕАЛІЗАЦІЯ ПРОЄКТУ

  1. Ініціалізація алгоритму
    
    public static class Dijkstra
    {
        /// 
        /// Знаходить вершину з мінімальною відстанню серед невідвіданих
        /// 
        /// Масив відстаней
        /// Масив відвіданих вершин
        /// Кількість вершин
        /// Індекс вершини з мінімальною відстанню
        private static int FindMinDistanceVertex(int[] distance, bool[] shortestPathTreeSet, int verticesCount)
        {
            int minDistance = int.MaxValue;
            int minIndex = 0;
    
            for (int v = 0; v < verticesCount; ++v)
            {
                if (!shortestPathTreeSet[v] && distance[v] <= minDistance)
                {
                    minDistance = distance[v];
                    minIndex = v;
                }
            }
            return minIndex;
        }
        /// 
        /// Виводить результати обчислення відстаней
        /// 
        /// Масив відстаней
        /// Кількість вершин
        private static void PrintDistances(int[] distance, int verticesCount)
        {
            Console.WriteLine("Вершина    Відстань від джерела");
            for (int i = 0; i < verticesCount; ++i)
                Console.WriteLine("{0}\t {1}", i, distance[i]);
        }
    }
    
  2. Основна логіка алгоритму
    
    /// 
    /// Реалізація алгоритму Дейкстри для знаходження найкоротших шляхів у графі
    /// 
    /// Матриця суміжності графа
    /// Початкова вершина
    /// Кількість вершин у графі
    public static void DijkstraAlgo(int[,] graph, int source, int verticesCount)
    {
        // Ініціалізація масивів
        int[] distance = new int[verticesCount];        // Масив найкоротших відстаней
        bool[] visitedVertices = new bool[verticesCount]; // Масив відвіданих вершин
    
        // Початкова ініціалізація
        for (int i = 0; i < verticesCount; i++)
        {
            distance[i] = int.MaxValue;          // Встановлюємо початкові відстані як "нескінченність"
            visitedVertices[i] = false;          // Позначаємо всі вершини як невідвідані
        }
        // Відстань від початкової вершини до самої себе завжди 0
        distance[source] = 0;
        // Основний цикл алгоритму
        for (int count = 0; count < verticesCount - 1; count++)
        {
            // Знаходимо вершину з мінімальною відстанню серед невідвіданих
            int currentVertex = FindMinDistanceVertex(distance, visitedVertices, verticesCount);
            visitedVertices[currentVertex] = true; // Позначаємо вершину як відвідану
    
            // Оновлюємо відстані до сусідніх вершин
            for (int neighbor = 0; neighbor < verticesCount; neighbor++)
            {
                // Перевіряємо умови для релаксації ребра:
                // 1. Вершина ще не відвідана
                // 2. Існує ребро між currentVertex і neighbor (graph[currentVertex, neighbor] != 0)
                // 3. Відстань до currentVertex не є нескінченністю
                // 4. Нова відстань коротша за поточну
                if (!visitedVertices[neighbor] && 
                    graph[currentVertex, neighbor] != 0 && 
                    distance[currentVertex] != int.MaxValue && 
                    distance[currentVertex] + graph[currentVertex, neighbor] < distance[neighbor])
                {
                    distance[neighbor] = distance[currentVertex] + graph[currentVertex, neighbor];
                }
            }
        }
    
        // Вивід результатів
        PrintDistances(distance, verticesCount);
    }
    
  3. Відновлення шляху
    
    using System;
    
    class Program
    {
        static void Main(string[] args)
        {
            // Матриця суміжності графа (ваги ребер)
            int[,] graph = {
                { 0, 6, 0, 0, 0, 0, 0, 0, 0 },    // Вершина 0
                { 6, 0, 9, 0, 0, 0, 0, 11, 0 },   // Вершина 1
                { 0, 9, 0, 5, 0, 6, 0, 0, 2 },     // Вершина 2
                { 0, 0, 5, 0, 9, 16, 0, 0, 0 },    // Вершина 3
                { 0, 0, 0, 9, 0, 10, 0, 0, 0 },    // Вершина 4
                { 0, 0, 6, 0, 10, 0, 2, 0, 0 },    // Вершина 5
                { 0, 0, 0, 16, 0, 2, 0, 1, 6 },    // Вершина 6
                { 9, 11, 0, 0, 0, 0, 1, 0, 5 },    // Вершина 7
                { 0, 0, 2, 0, 0, 0, 6, 5, 0 }      // Вершина 8
            };
    
            Console.WriteLine("Починаємо обчислення найкоротших шляхів за алгоритмом Дейкстри");
            Console.WriteLine("-----------------------------------------------------------");
            
            // Виклик алгоритму Дейкстри з початковою вершиною 0
            Dijkstra.DijkstraAlgo(graph, 0, 9);
    
            Console.WriteLine("\nНатисніть будь-яку клавішу для виходу...");
            Console.ReadKey();
        }
    }
    

  4. Приклад роботи алгоритму

WF-проект з візуалізацією найкоротшого шляху за алгоритмом Дейкстри.

Використовано наш проект візуалізації графа як основу.

namespace GraphVisualization
{
    partial class Form1
    {
        /// <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.menuStrip1 = new System.Windows.Forms.MenuStrip();
            this.graphToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.newGraphToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.clearToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.matrixToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.generateMatrixToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.findShortestPathToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.pictureBox = new System.Windows.Forms.PictureBox();
            this.dataGridViewPoints = new System.Windows.Forms.DataGridView();
            this.dataGridViewMatrix = new System.Windows.Forms.DataGridView();
            this.labelPoints = new System.Windows.Forms.Label();
            this.labelMatrix = new System.Windows.Forms.Label();
            this.comboBoxStartVertex = new System.Windows.Forms.ComboBox();
            this.comboBoxEndVertex = new System.Windows.Forms.ComboBox();
            this.labelStartVertex = new System.Windows.Forms.Label();
            this.labelEndVertex = new System.Windows.Forms.Label();
            this.menuStrip1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridViewPoints)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridViewMatrix)).BeginInit();
            this.SuspendLayout();
            // 
            // menuStrip1
            // 
            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.graphToolStripMenuItem,
            this.matrixToolStripMenuItem});
            this.menuStrip1.Location = new System.Drawing.Point(0, 0);
            this.menuStrip1.Name = "menuStrip1";
            this.menuStrip1.Size = new System.Drawing.Size(980, 24);
            this.menuStrip1.TabIndex = 0;
            this.menuStrip1.Text = "menuStrip1";
            // 
            // graphToolStripMenuItem
            // 
            this.graphToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.newGraphToolStripMenuItem,
            this.clearToolStripMenuItem});
            this.graphToolStripMenuItem.Name = "graphToolStripMenuItem";
            this.graphToolStripMenuItem.Size = new System.Drawing.Size(51, 20);
            this.graphToolStripMenuItem.Text = "Graph";
            // 
            // newGraphToolStripMenuItem
            // 
            this.newGraphToolStripMenuItem.Name = "newGraphToolStripMenuItem";
            this.newGraphToolStripMenuItem.Size = new System.Drawing.Size(121, 22);
            this.newGraphToolStripMenuItem.Text = "Generate";
            this.newGraphToolStripMenuItem.Click += new System.EventHandler(this.newGraphToolStripMenuItem_Click);
            // 
            // clearToolStripMenuItem
            // 
            this.clearToolStripMenuItem.Name = "clearToolStripMenuItem";
            this.clearToolStripMenuItem.Size = new System.Drawing.Size(121, 22);
            this.clearToolStripMenuItem.Text = "Clear";
            this.clearToolStripMenuItem.Click += new System.EventHandler(this.clearToolStripMenuItem_Click);
            // 
            // matrixToolStripMenuItem
            // 
            this.matrixToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.generateMatrixToolStripMenuItem,
            this.findShortestPathToolStripMenuItem});
            this.matrixToolStripMenuItem.Name = "matrixToolStripMenuItem";
            this.matrixToolStripMenuItem.Size = new System.Drawing.Size(53, 20);
            this.matrixToolStripMenuItem.Text = "Matrix";
            // 
            // generateMatrixToolStripMenuItem
            // 
            this.generateMatrixToolStripMenuItem.Name = "generateMatrixToolStripMenuItem";
            this.generateMatrixToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
            this.generateMatrixToolStripMenuItem.Text = "Generate";
            this.generateMatrixToolStripMenuItem.Click += new System.EventHandler(this.generateMatrixToolStripMenuItem_Click);
            // 
            // findShortestPathToolStripMenuItem
            // 
            this.findShortestPathToolStripMenuItem.Name = "findShortestPathToolStripMenuItem";
            this.findShortestPathToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
            this.findShortestPathToolStripMenuItem.Text = "Find Shortest Path";
            this.findShortestPathToolStripMenuItem.Click += new System.EventHandler(this.findShortestPathToolStripMenuItem_Click);
            // 
            // pictureBox
            // 
            this.pictureBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pictureBox.Location = new System.Drawing.Point(12, 27);
            this.pictureBox.Name = "pictureBox";
            this.pictureBox.Size = new System.Drawing.Size(400, 400);
            this.pictureBox.TabIndex = 1;
            this.pictureBox.TabStop = false;
            this.pictureBox.Click += new System.EventHandler(this.pictureBox_Click);
            // 
            // dataGridViewPoints
            // 
            this.dataGridViewPoints.AllowUserToAddRows = false;
            this.dataGridViewPoints.AllowUserToDeleteRows = false;
            this.dataGridViewPoints.AllowUserToResizeColumns = false;
            this.dataGridViewPoints.AllowUserToResizeRows = false;
            this.dataGridViewPoints.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridViewPoints.Enabled = false;
            this.dataGridViewPoints.Location = new System.Drawing.Point(420, 43);
            this.dataGridViewPoints.Name = "dataGridViewPoints";
            this.dataGridViewPoints.Size = new System.Drawing.Size(160, 384);
            this.dataGridViewPoints.TabIndex = 2;
            // 
            // dataGridViewMatrix
            // 
            this.dataGridViewMatrix.AllowUserToAddRows = false;
            this.dataGridViewMatrix.AllowUserToDeleteRows = false;
            this.dataGridViewMatrix.AllowUserToResizeColumns = false;
            this.dataGridViewMatrix.AllowUserToResizeRows = false;
            this.dataGridViewMatrix.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridViewMatrix.Location = new System.Drawing.Point(586, 43);
            this.dataGridViewMatrix.Name = "dataGridViewMatrix";
            this.dataGridViewMatrix.Size = new System.Drawing.Size(384, 384);
            this.dataGridViewMatrix.TabIndex = 3;
            // 
            // labelPoints
            // 
            this.labelPoints.AutoSize = true;
            this.labelPoints.Location = new System.Drawing.Point(482, 27);
            this.labelPoints.Name = "labelPoints";
            this.labelPoints.Size = new System.Drawing.Size(36, 13);
            this.labelPoints.TabIndex = 4;
            this.labelPoints.Text = "Points";
            // 
            // labelMatrix
            // 
            this.labelMatrix.AutoSize = true;
            this.labelMatrix.Location = new System.Drawing.Point(735, 27);
            this.labelMatrix.Name = "labelMatrix";
            this.labelMatrix.Size = new System.Drawing.Size(87, 13);
            this.labelMatrix.TabIndex = 5;
            this.labelMatrix.Text = "Adjacency matrix";
            // 
            // comboBoxStartVertex
            // 
            this.comboBoxStartVertex.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.comboBoxStartVertex.FormattingEnabled = true;
            this.comboBoxStartVertex.Location = new System.Drawing.Point(420, 450);
            this.comboBoxStartVertex.Name = "comboBoxStartVertex";
            this.comboBoxStartVertex.Size = new System.Drawing.Size(160, 21);
            this.comboBoxStartVertex.TabIndex = 6;
            this.comboBoxStartVertex.SelectedIndexChanged += new System.EventHandler(this.comboBoxStartVertex_SelectedIndexChanged);
            // 
            // comboBoxEndVertex
            // 
            this.comboBoxEndVertex.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.comboBoxEndVertex.FormattingEnabled = true;
            this.comboBoxEndVertex.Location = new System.Drawing.Point(586, 450);
            this.comboBoxEndVertex.Name = "comboBoxEndVertex";
            this.comboBoxEndVertex.Size = new System.Drawing.Size(160, 21);
            this.comboBoxEndVertex.TabIndex = 7;
            this.comboBoxEndVertex.SelectedIndexChanged += new System.EventHandler(this.comboBoxEndVertex_SelectedIndexChanged);
            // 
            // labelStartVertex
            // 
            this.labelStartVertex.AutoSize = true;
            this.labelStartVertex.Location = new System.Drawing.Point(420, 434);
            this.labelStartVertex.Name = "labelStartVertex";
            this.labelStartVertex.Size = new System.Drawing.Size(62, 13);
            this.labelStartVertex.TabIndex = 8;
            this.labelStartVertex.Text = "Start Vertex";
            // 
            // labelEndVertex
            // 
            this.labelEndVertex.AutoSize = true;
            this.labelEndVertex.Location = new System.Drawing.Point(586, 434);
            this.labelEndVertex.Name = "labelEndVertex";
            this.labelEndVertex.Size = new System.Drawing.Size(59, 13);
            this.labelEndVertex.TabIndex = 9;
            this.labelEndVertex.Text = "End Vertex";
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(980, 483);
            this.Controls.Add(this.labelEndVertex);
            this.Controls.Add(this.labelStartVertex);
            this.Controls.Add(this.comboBoxEndVertex);
            this.Controls.Add(this.comboBoxStartVertex);
            this.Controls.Add(this.labelMatrix);
            this.Controls.Add(this.labelPoints);
            this.Controls.Add(this.dataGridViewMatrix);
            this.Controls.Add(this.dataGridViewPoints);
            this.Controls.Add(this.pictureBox);
            this.Controls.Add(this.menuStrip1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.MainMenuStrip = this.menuStrip1;
            this.Name = "Form1";
            this.Text = "Graph Visualization - Dijkstra Algorithm";
            this.menuStrip1.ResumeLayout(false);
            this.menuStrip1.PerformLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridViewPoints)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridViewMatrix)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.MenuStrip menuStrip1;
        private System.Windows.Forms.ToolStripMenuItem graphToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem newGraphToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem clearToolStripMenuItem;
        private System.Windows.Forms.PictureBox pictureBox;
        private System.Windows.Forms.DataGridView dataGridViewPoints;
        private System.Windows.Forms.DataGridView dataGridViewMatrix;
        private System.Windows.Forms.Label labelPoints;
        private System.Windows.Forms.Label labelMatrix;
        private System.Windows.Forms.ToolStripMenuItem matrixToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem generateMatrixToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem findShortestPathToolStripMenuItem;
        private System.Windows.Forms.ComboBox comboBoxStartVertex;
        private System.Windows.Forms.ComboBox comboBoxEndVertex;
        private System.Windows.Forms.Label labelStartVertex;
        private System.Windows.Forms.Label labelEndVertex;
    }
}

Клас Form1

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace GraphVisualization
{
    // Структура для представлення ребра графа
    struct Edge
    {
        public MyPoint Point1;  // Перша точка ребра
        public MyPoint Point2;  // Друга точка ребра
        public int Weight;      // Вага ребра (довжина)

        // Конструктор ребра
        public Edge(MyPoint point1, MyPoint point2, int weight)
        {
            Point1 = point1;
            Point2 = point2;
            Weight = weight;
        }
    }

    // Структура для представлення точки графа
    struct MyPoint
    {
        public int Id;  // Ідентифікатор точки
        public int X;   // Координата X
        public int Y;   // Координата Y

        // Конструктор точки
        public MyPoint(int id, int x, int y)
        {
            Id = id;
            X = x;
            Y = y;
        }
    }

    // Клас для зберігання інформації про вершину в алгоритмі Дейкстри
    class DijkstraNode : IComparable<DijkstraNode>
    {
        public int VertexId { get; set; }
        public int Distance { get; set; }
        public int PreviousVertex { get; set; }

        public DijkstraNode(int vertexId, int distance, int previousVertex = -1)
        {
            VertexId = vertexId;
            Distance = distance;
            PreviousVertex = previousVertex;
        }

        public int CompareTo(DijkstraNode other)
        {
            return Distance.CompareTo(other.Distance);
        }
    }

    // Головна форма програми
    public partial class Form1 : Form
    {
        private Bitmap bitmap;           // Бітова карта для малювання
        private List<MyPoint> points = new List<MyPoint>();  // Список точок
        private List<Edge> edges = new List<Edge>();      // Список ребер
        private List<Edge> shortestPathEdges = new List<Edge>(); // Ребра найкоротшого шляху
        private Graphics graphics;       // Графічний контекст
        private int startVertexId = -1;  // Початкова вершина для алгоритму Дейкстри
        private int endVertexId = -1;    // Кінцева вершина для алгоритму Дейкстри

        // Константи для налаштування відображення
        private const int Radius = 6;
        private const int EdgeThickness = 2;
        private const int ShortestPathThickness = 3;
        private const int PointLabelFontSize = 12;
        private const int EdgeLabelFontSize = 10;
        private const string FontName = "Arial";

        // Конструктор форми
        public Form1()
        {
            InitializeComponent();
            // Ініціалізація графічних компонентів
            bitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
            graphics = Graphics.FromImage(bitmap);
            
            // Налаштування DataGridView для точок
            dataGridViewPoints.Columns.Add("pointNumber", "№");
            dataGridViewPoints.Columns.Add("pointСoordinates", "Point");
            dataGridViewPoints.Columns[0].Width = 30;
            dataGridViewPoints.Columns[1].Width = 70;
            
            // Налаштування DataGridView для матриці
            dataGridViewMatrix.RowHeadersWidth = 50;

            // Додавання випадаючих списків для вибору вершин
            comboBoxStartVertex.DropDownStyle = ComboBoxStyle.DropDownList;
            comboBoxEndVertex.DropDownStyle = ComboBoxStyle.DropDownList;
        }

        // Обчислення довжини ребра між двома точками
        private int EdgeLength(MyPoint point1, MyPoint point2)
        {
            return (int)Math.Sqrt(Math.Pow(point1.X - point2.X, 2) + Math.Pow(point1.Y - point2.Y, 2));
        }

        // Малювання точки на графіці
        private void DrawPoint(MyPoint point, string text, Color color)
        {
            // Малювання кружечка
            graphics.FillEllipse(new SolidBrush(color),
                point.X - Radius, point.Y - Radius, Radius * 2, Radius * 2);
            // Додавання текстової мітки
            graphics.DrawString(text, new Font(FontName, PointLabelFontSize),
                new SolidBrush(Color.Black), point.X + Radius, point.Y + Radius);
            pictureBox.Image = bitmap;
        }

        // Заповнення матриці суміжності
        private void FillMatrix(int number)
        {
            for (int i = 0; i < points.Count; i++)
            {
                for (int j = 0; j < points.Count; j++)
                {
                    dataGridViewMatrix.Rows[i].Cells[j].Value = (i < j) ? number : 0;
                }
            }
        }

        // Генерація матриці суміжності
        private void GenerateMatrix()
        {
            ClearMatrix();

            // Додавання колонок до матриці
            foreach (var point in points)
            {
                DataGridViewColumn column = new DataGridViewTextBoxColumn();
                column.Name = $"{point.Id}";
                column.Width = 25;
                dataGridViewMatrix.Columns.Add(column);
            }

            // Додавання рядків до матриці
            foreach (var point in points)
            {
                dataGridViewMatrix.Rows.Add();
                dataGridViewMatrix.Rows[dataGridViewMatrix.Rows.Count - 1].HeaderCell.Value = $"{point.Id}";
            }

            FillMatrix(1);

            // Центрування тексту в комірках
            foreach (DataGridViewColumn column in dataGridViewMatrix.Columns)
            {
                column.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            }

            // Оновлення випадаючих списків
            UpdateVertexComboBoxes();
        }

        // Оновлення випадаючих списків для вибору вершин
        private void UpdateVertexComboBoxes()
        {
            comboBoxStartVertex.Items.Clear();
            comboBoxEndVertex.Items.Clear();

            foreach (var point in points)
            {
                comboBoxStartVertex.Items.Add($"{point.Id}");
                comboBoxEndVertex.Items.Add($"{point.Id}");
            }

            if (comboBoxStartVertex.Items.Count > 0)
                comboBoxStartVertex.SelectedIndex = 0;
            if (comboBoxEndVertex.Items.Count > 0)
                comboBoxEndVertex.SelectedIndex = 0;
        }

        // Генерація списку ребер на основі матриці
        private void GenerateEdges()
        {
            edges.Clear();
            for (int i = 0; i < points.Count; i++)
            {
                for (int j = 0; j < points.Count; j++)
                {
                    if (i != j && Convert.ToInt32(dataGridViewMatrix.Rows[i].Cells[j].Value) == 1)
                    {
                        MyPoint p1 = new MyPoint(points[i].Id, points[i].X, points[i].Y);
                        MyPoint p2 = new MyPoint(points[j].Id, points[j].X, points[j].Y);
                        edges.Add(new Edge(p1, p2, EdgeLength(p1, p2)));
                    }
                }
            }
        }

        // Алгоритм Дейкстри для знаходження найкоротшого шляху
        private void DijkstraAlgorithm()
        {
            if (edges.Count == 0 || startVertexId == -1 || endVertexId == -1)
                return;

            // Ініціалізація відстаней
            int[] distances = new int[points.Count];
            int[] previous = new int[points.Count];
            bool[] visited = new bool[points.Count];

            for (int i = 0; i < points.Count; i++)
            {
                distances[i] = int.MaxValue;
                previous[i] = -1;
                visited[i] = false;
            }

            distances[startVertexId] = 0;

            // Пріоритетна черга для вершин
            var priorityQueue = new SortedSet<DijkstraNode>(Comparer<DijkstraNode>.Create((x, y) => 
                x.Distance != y.Distance ? x.Distance.CompareTo(y.Distance) : x.VertexId.CompareTo(y.VertexId)));

            priorityQueue.Add(new DijkstraNode(startVertexId, 0));

            while (priorityQueue.Count > 0)
            {
                var currentNode = priorityQueue.Min;
                priorityQueue.Remove(currentNode);

                int currentVertex = currentNode.VertexId;
                if (visited[currentVertex]) continue;
                visited[currentVertex] = true;

                // Якщо досягли кінцевої вершини
                if (currentVertex == endVertexId)
                    break;

                // Отримання сусідів поточної вершини
                foreach (var edge in edges)
                {
                    int neighbor = -1;
                    if (edge.Point1.Id == currentVertex)
                        neighbor = edge.Point2.Id;
                    else if (edge.Point2.Id == currentVertex)
                        neighbor = edge.Point1.Id;

                    if (neighbor != -1 && !visited[neighbor])
                    {
                        int newDistance = distances[currentVertex] + edge.Weight;
                        if (newDistance < distances[neighbor])
                        {
                            distances[neighbor] = newDistance;
                            previous[neighbor] = currentVertex;
                            priorityQueue.Add(new DijkstraNode(neighbor, newDistance));
                        }
                    }
                }
            }

            // Відновлення шляху
            shortestPathEdges.Clear();
            int current = endVertexId;
            while (previous[current] != -1)
            {
                int prev = previous[current];
                // Знаходження ребра між current і prev
                var pathEdge = edges.FirstOrDefault(e =>
                    (e.Point1.Id == current && e.Point2.Id == prev) ||
                    (e.Point1.Id == prev && e.Point2.Id == current));

                if (pathEdge.Point1.Id != -1) // Перевірка, що ребро знайдено
                {
                    shortestPathEdges.Add(pathEdge);
                }
                current = prev;
            }

            // Виведення результату
            if (distances[endVertexId] != int.MaxValue)
            {
                MessageBox.Show($"Найкоротший шлях від {startVertexId} до {endVertexId}: {distances[endVertexId]}");
            }
            else
            {
                MessageBox.Show("Шлях не знайдено");
            }
        }

        // Малювання всіх точок
        private void DrawPoints()
        {
            for (int i = 0; i < points.Count; i++)
            {
                Color pointColor = Color.Red;
                if (i == startVertexId) pointColor = Color.Green;
                else if (i == endVertexId) pointColor = Color.Blue;
                
                DrawPoint(points[i], $"{i}", pointColor);
            }
        }

        // Малювання всіх ребер
        private void DrawEdges()
        {
            foreach (var edge in edges)
            {
                // Перевірка, чи ребро належить до найкоротшого шляху
                bool isShortestPath = shortestPathEdges.Any(pe =>
                    (pe.Point1.Id == edge.Point1.Id && pe.Point2.Id == edge.Point2.Id) ||
                    (pe.Point1.Id == edge.Point2.Id && pe.Point2.Id == edge.Point1.Id));

                Color edgeColor = isShortestPath ? Color.Red : Color.Chartreuse;
                int thickness = isShortestPath ? ShortestPathThickness : EdgeThickness;

                MyPoint p1 = edge.Point1;
                MyPoint p2 = edge.Point2;
                var label = edge.Weight.ToString();
                var font = new Font(FontName, EdgeLabelFontSize);
                var size = graphics.MeasureString(label, font, pictureBox.Size);
                
                // Малювання лінії ребра
                graphics.DrawLine(new Pen(edgeColor, thickness), 
                    new Point(p1.X, p1.Y), new Point(p2.X, p2.Y));
                
                // Додавання мітки з довжиною ребра
                graphics.DrawString(label,
                    font, new SolidBrush(Color.Black),
                    (p1.X + p2.X) / 2 - size.Width / 2, (p1.Y + p2.Y) / 2 - size.Height / 2);
            }
        }

        // Очищення графа
        private void ClearGraph()
        {
            graphics.Clear(Color.White);
            pictureBox.Image = bitmap;
            points.Clear();
            dataGridViewPoints.Rows.Clear();
            shortestPathEdges.Clear();
            startVertexId = -1;
            endVertexId = -1;
            comboBoxStartVertex.Items.Clear();
            comboBoxEndVertex.Items.Clear();
        }

        // Очищення матриці
        private void ClearMatrix()
        {
            dataGridViewMatrix.Rows.Clear();
            dataGridViewMatrix.Columns.Clear();
        }

        // Обробник кліку на pictureBox для додавання нової точки
        private void pictureBox_Click(object sender, EventArgs e)
        {
            int x = MousePosition.X - Location.X - pictureBox.Location.X - 8;
            int y = MousePosition.Y - Location.Y - pictureBox.Location.Y - 32;
            points.Add(new MyPoint(points.Count, x, y));
            dataGridViewPoints.Rows.Add(points[points.Count - 1].Id, $"({x}; {y})");
            DrawPoint(points[points.Count - 1], $"{points.Count - 1}", Color.Red);
        }

        // Обробник зміни вибору початкової вершини
        private void comboBoxStartVertex_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBoxStartVertex.SelectedIndex >= 0)
            {
                startVertexId = comboBoxStartVertex.SelectedIndex;
                Invalidate();
            }
        }

        // Обробник зміни вибору кінцевої вершини
        private void comboBoxEndVertex_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBoxEndVertex.SelectedIndex >= 0)
            {
                endVertexId = comboBoxEndVertex.SelectedIndex;
                Invalidate();
            }
        }

        // Обробник меню для генерації нового графа
        private void newGraphToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (dataGridViewMatrix.Rows.Count < dataGridViewPoints.Rows.Count)
                GenerateMatrix();
            GenerateEdges();
            DrawEdges();
            Invalidate();
        }

        // Обробник меню для очищення всього
        private void clearToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ClearGraph();
            ClearMatrix();
        }

        // Обробник меню для генерації матриці
        private void generateMatrixToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GenerateMatrix();
        }

        // Обробник меню для знаходження найкоротшого шляху
        private void findShortestPathToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (dataGridViewMatrix.Rows.Count < dataGridViewPoints.Rows.Count)
                GenerateMatrix();
            GenerateEdges();
            DijkstraAlgorithm();
            Invalidate();
        }

        // Перевизначення методу малювання форми
        protected override void OnPaint(PaintEventArgs e)
        {
            graphics.Clear(Color.White);
            pictureBox.Image = bitmap;
            DrawPoints();
            DrawEdges();
            base.OnPaint(e);
        }
    }
}

Назад Вперед Зміст