pitchcontrol
3/1/2019 - 9:08 AM

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

namespace Katas.Shorts
{
    public class Edge
    {
        public Edge(Vertex firstVertex, Vertex secondVertex, double weight)
        {
            FirstVertex = firstVertex;
            SecondVertex = secondVertex;
            Weight = weight;
        }

        public Vertex FirstVertex { get; }
        public Vertex SecondVertex { get; }
        public double Weight { get; }

        public override string ToString()
        {
            return $"{FirstVertex}-{Weight}-{SecondVertex}";
        }
    }

    public class Vertex : IEnumerable<Vertex>
    {
        private readonly List<Edge> _neighbours = new List<Edge>();
        public string Name { get; set; }

        public double Cost { get; set; } = Double.PositiveInfinity;

        public bool Visited { get; set; }

        public Vertex(string name)
        {
            Name = name;
        }

        private void AddNeighbour(Edge edge)
        {
            _neighbours.Add(edge);
        }

        public void AddNeighbour(Vertex neighbour, double weight)
        {
            var edge = new Edge(this, neighbour, weight);
            _neighbours.Add(edge);
            neighbour.AddNeighbour(edge);
        }

        public Vertex FindLowestCost()
        {
            return _neighbours.Select(i => i.FirstVertex == this ? i.SecondVertex : i.FirstVertex)
                .Where(i => i.Visited == false)
                .OrderBy(i => i.Cost)
                .FirstOrDefault();
        }

        public IEnumerator<Vertex> GetEnumerator()
        {
            foreach (Edge neighbour in _neighbours)
            {
                yield return neighbour.FirstVertex == this ? neighbour.SecondVertex : neighbour.FirstVertex;
            }
        }

        public double GetWeightBetween(Vertex oppositeVertex)
        {
            var edge = _neighbours.FirstOrDefault(i =>
                (i.FirstVertex == this && oppositeVertex == i.SecondVertex) ||
                (i.SecondVertex == this && oppositeVertex == i.FirstVertex));
            if (edge == null)
                throw new InvalidOperationException();

            return edge.Weight;
        }


        public override string ToString()
        {
            return Name;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public class Dijkstra
    {
        private static List<string> GetPath(Dictionary<string, string> parents)
        {
            string node = "fin";
            var list = new List<string>();
            while (parents.ContainsKey(node))
            {
                list.Add(node);
                node = parents[node];
            }

            list.Add(node);
            list.Reverse();
            return list;
        }

        public static void Show()
        {
            var startNode = new Vertex("start");
            startNode.Visited = true;
            var a = new Vertex("a");
            var b = new Vertex("b");
            var c = new Vertex("c");
            var d = new Vertex("d");
            var e = new Vertex("e");

            var fin = new Vertex("fin");

            startNode.AddNeighbour(a, 6);
            a.Cost = 6;
            startNode.AddNeighbour(b, 2);
            b.Cost = 2;
            a.AddNeighbour(b, 3);

            c.AddNeighbour(a, 1);
            c.AddNeighbour(b, 5);

            c.AddNeighbour(d, 2);
            c.AddNeighbour(e, 3);
            d.AddNeighbour(e, 1);

            fin.AddNeighbour(d, 4);
            fin.AddNeighbour(e, 5);

            var parents = new Dictionary<string, string>();


            var node = startNode.FindLowestCost();
            while (node != null)
            {
                var cost = node.Cost;
                foreach (Vertex vertex in node)
                {
                    var newCost = cost + vertex.GetWeightBetween(node);
                    if (vertex.Cost > newCost)
                    {
                        vertex.Cost = newCost;
                        parents[vertex.Name] = node.Name;
                    }
                }

                node.Visited = true;
                node = node.FindLowestCost();
            }

            Console.WriteLine($"Path: {string.Join("->", GetPath(parents))}");
        }
    }
}