經典算法題每日演練——第十七題 Dijkstra算法


 

      或許在生活中,經常會碰到針對某一個問題,在眾多的限制條件下,如何去尋找一個最優解?可能大家想到了很多諸如“線性規划”,“動態規划”

這些經典策略,當然有的問題我們可以用貪心來尋求整體最優解,在圖論中一個典型的貪心法求最優解的例子就莫過於“最短路徑”的問題。

 

一:概序

   從下圖中我要尋找V0到V3的最短路徑,你會發現通往他們的兩點路徑有很多:V0->V4->V3,V0->V1->V3,當然你會認為前者是你要找的最短

路徑,那如果說圖的頂點非常多,你還會這么輕易的找到嗎?下面我們就要將剛才我們那點貪心的思維系統的整理下。

二:構建

    如果大家已經了解Prim算法,那么Dijkstra算法只是在它的上面延伸了下,其實也是很簡單的。

1.邊節點

  這里有點不一樣的地方就是我在邊上面定義一個vertexs來記錄貪心搜索到某一個節點時曾經走過的節點,比如從V0貪心搜索到V3時,我們V3

的vertexs可能存放着V0,V4,V3這些曾今走過的節點,或許最后這三個節點就是我們要尋找的最短路徑。

 1 #region 邊的信息
 2         /// <summary>
 3         /// 邊的信息
 4         /// </summary>
 5         public class Edge
 6         {
 7             //開始邊
 8             public int startEdge;
 9 
10             //結束邊
11             public int endEdge;
12 
13             //權重
14             public int weight;
15 
16             //是否使用
17             public bool isUse;
18 
19             //累計頂點
20             public HashSet<int> vertexs = new HashSet<int>();
21         }
22         #endregion

2.Dijkstra算法

首先我們分析下Dijkstra算法的步驟:

有集合M={V0,V1,V2,V3,V4}這樣5個元素,我們用

TempVertex表示該頂點是否使用。

Weight表示該Path的權重(默認都為MaxValue)。

Path表示該頂點的總權重。

①. 從集合M中挑選頂點V0為起始點。給V0的所有鄰接點賦值,要賦值的前提是要賦值的weight要小於原始的weight,並且排除已經訪問過

     的頂點,然后挑選當前最小的weight作為下一次貪心搜索的起點,就這樣V0V1為挑選為最短路徑,如圖2。

②. 我們繼續從V1這個頂點開始給鄰接點以同樣的方式賦值,最后我們發現V0V4為最短路徑。也就是圖3。

。。。

③. 最后所有頂點的最短路徑就這樣求出來了 。

 1 #region Dijkstra算法
 2         /// <summary>
 3         /// Dijkstra算法
 4         /// </summary>
 5         public Dictionary<int, Edge> Dijkstra()
 6         {
 7             //收集頂點的相鄰邊
 8             Dictionary<int, Edge> dic_edges = new Dictionary<int, Edge>();
 9 
10             //weight=MaxValue:標識沒有邊
11             for (int i = 0; i < graph.vertexsNum; i++)
12             {
13                 //起始邊
14                 var startEdge = i;
15 
16                 dic_edges.Add(startEdge, new Edge() { weight = int.MaxValue });
17             }
18 
19             //取第一個頂點
20             var start = 0;
21 
22             for (int i = 0; i < graph.vertexsNum; i++)
23             {
24                 //標記該頂點已經使用過
25                 dic_edges[start].isUse = true;
26 
27                 for (int j = 1; j < graph.vertexsNum; j++)
28                 {
29                     var end = j;
30 
31                     //取到相鄰邊的權重
32                     var weight = graph.edges[start, end];
33 
34                     //賦較小的權重
35                     if (weight < dic_edges[end].weight)
36                     {
37                         //與上一個頂點的權值累加
38                         var totalweight = dic_edges[start].weight == int.MaxValue ? weight : dic_edges[start].weight + weight;
39 
40                         if (totalweight < dic_edges[end].weight)
41                         {
42                             //將該頂點的相鄰邊加入到集合中
43                             dic_edges[end] = new Edge()
44                             {
45                                 startEdge = start,
46                                 endEdge = end,
47                                 weight = totalweight
48                             };
49 
50                             //將上一個邊的節點的vertex累加
51                             dic_edges[end].vertexs = new HashSet<int>(dic_edges[start].vertexs);
52 
53                             dic_edges[end].vertexs.Add(start);
54                             dic_edges[end].vertexs.Add(end);
55                         }
56                     }
57                 }
58 
59                 var min = int.MaxValue;
60 
61                 //下一個進行比較的頂點
62                 int minkey = 0;
63 
64                 //取start鄰接邊中的最小值
65                 foreach (var key in dic_edges.Keys)
66                 {
67                     //取當前 最小的 key(使用過的除外)
68                     if (min > dic_edges[key].weight && !dic_edges[key].isUse)
69                     {
70                         min = dic_edges[key].weight;
71                         minkey = key;
72                     }
73                 }
74 
75                 //從鄰接邊的頂點再開始找
76                 start = minkey;
77             }
78 
79             return dic_edges;
80         }
81         #endregion

 

總的代碼:復雜度很爛O(N2)。。。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.IO;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    public class Program
    {
        public static void Main()
        {
            Dictionary<int, string> dic = new Dictionary<int, string>();

            MatrixGraph graph = new MatrixGraph();

            graph.Build();

            var result = graph.Dijkstra();

            Console.WriteLine("各節點的最短路徑為:");

            foreach (var key in result.Keys)
            {
                Console.WriteLine("{0}", string.Join("->", result[key].vertexs));
            }

            Console.Read();
        }
    }

    #region 定義矩陣節點
    /// <summary>
    /// 定義矩陣節點
    /// </summary>
    public class MatrixGraph
    {
        Graph graph = new Graph();

        public class Graph
        {
            /// <summary>
            /// 頂點信息
            /// </summary>
            public int[] vertexs;

            /// <summary>
            /// 邊的條數
            /// </summary>
            public int[,] edges;

            /// <summary>
            /// 頂點個數
            /// </summary>
            public int vertexsNum;

            /// <summary>
            /// 邊的個數
            /// </summary>
            public int edgesNum;
        }

        #region 矩陣的構建
        /// <summary>
        /// 矩陣的構建
        /// </summary>
        public void Build()
        {
            //頂點數
            graph.vertexsNum = 5;

            //邊數
            graph.edgesNum = 6;

            graph.vertexs = new int[graph.vertexsNum];

            graph.edges = new int[graph.vertexsNum, graph.vertexsNum];

            //構建二維數組
            for (int i = 0; i < graph.vertexsNum; i++)
            {
                //頂點
                graph.vertexs[i] = i;

                for (int j = 0; j < graph.vertexsNum; j++)
                {
                    graph.edges[i, j] = int.MaxValue;
                }
            }

            //定義 6 條邊
            graph.edges[0, 1] = graph.edges[1, 0] = 2;
            graph.edges[0, 2] = graph.edges[2, 0] = 5;
            graph.edges[0, 4] = graph.edges[4, 0] = 3;
            graph.edges[1, 3] = graph.edges[3, 1] = 4;
            graph.edges[2, 4] = graph.edges[4, 2] = 5;
            graph.edges[3, 4] = graph.edges[4, 3] = 2;

        }
        #endregion

        #region 邊的信息
        /// <summary>
        /// 邊的信息
        /// </summary>
        public class Edge
        {
            //開始邊
            public int startEdge;

            //結束邊
            public int endEdge;

            //權重
            public int weight;

            //是否使用
            public bool isUse;

            //累計頂點
            public HashSet<int> vertexs = new HashSet<int>();
        }
        #endregion

        #region Dijkstra算法
        /// <summary>
        /// Dijkstra算法
        /// </summary>
        public Dictionary<int, Edge> Dijkstra()
        {
            //收集頂點的相鄰邊
            Dictionary<int, Edge> dic_edges = new Dictionary<int, Edge>();

            //weight=MaxValue:標識沒有邊
            for (int i = 0; i < graph.vertexsNum; i++)
            {
                //起始邊
                var startEdge = i;

                dic_edges.Add(startEdge, new Edge() { weight = int.MaxValue });
            }

            //取第一個頂點
            var start = 0;

            for (int i = 0; i < graph.vertexsNum; i++)
            {
                //標記該頂點已經使用過
                dic_edges[start].isUse = true;

                for (int j = 1; j < graph.vertexsNum; j++)
                {
                    var end = j;

                    //取到相鄰邊的權重
                    var weight = graph.edges[start, end];

                    //賦較小的權重
                    if (weight < dic_edges[end].weight)
                    {
                        //與上一個頂點的權值累加
                        var totalweight = dic_edges[start].weight == int.MaxValue ? weight : dic_edges[start].weight + weight;

                        if (totalweight < dic_edges[end].weight)
                        {
                            //將該頂點的相鄰邊加入到集合中
                            dic_edges[end] = new Edge()
                            {
                                startEdge = start,
                                endEdge = end,
                                weight = totalweight
                            };

                            //將上一個邊的節點的vertex累加
                            dic_edges[end].vertexs = new HashSet<int>(dic_edges[start].vertexs);

                            dic_edges[end].vertexs.Add(start);
                            dic_edges[end].vertexs.Add(end);
                        }
                    }
                }

                var min = int.MaxValue;

                //下一個進行比較的頂點
                int minkey = 0;

                //取start鄰接邊中的最小值
                foreach (var key in dic_edges.Keys)
                {
                    //取當前 最小的 key(使用過的除外)
                    if (min > dic_edges[key].weight && !dic_edges[key].isUse)
                    {
                        min = dic_edges[key].weight;
                        minkey = key;
                    }
                }

                //從鄰接邊的頂點再開始找
                start = minkey;
            }

            return dic_edges;
        }
        #endregion
    }
    #endregion
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM