-
Dijkstra(迪杰斯特拉)算法
-
解决的问题
用户指定一个顶点(V_k),求出(V_i|_1^N)与(V_k)的最短路径及其长度。
-
算法的核心
说到底它是一种“贪心算法”,通过在每一步做出局部最优决策来解决问题,希望找到全局最小值。
通俗地说就是:我们
只有
按照“从(V_k)出发找相邻的顶点,然后挑个最短的(V_{min1}),并找离其最短的(V_{min2})”这样的方法寻找时,才可能
得到最短路径。这是我们寻找最短路径的必要条件
:[min orm{overrightarrow{V_kV_i}}=min orm{overrightarrow{V_kV_{min1}}}+cdots+min orm{overrightarrow{V_{min M-1}V_i}}=sum_{j=min1}^{min M}min orm{overrightarrow{V_{j-1}V_{j}}} ]这就是将所有的局部最短相加,
期望
得到一个全局最短。但是这样显然是不严谨的,因为很可能出现两边之和大于第三边的情况,故而需要增加判定条件(必要转充要的转化条件)[ orm{overrightarrow{V_kV_i}}= orm{overrightarrow{V_kV_i}} le orm{overrightarrow{V_kV_{min j}}}+ orm{overrightarrow{V_{min j}V_i}} ? orm{overrightarrow{V_kV_i}} : orm{overrightarrow{V_kV_{min j}}}+ orm{overrightarrow{V_{min j}V_i}} ]如果增加了一个中转点之后路径变小了则将中转点填入最短路径计算中,否则维持原路径。这个中转点的确定依据是:当前离(V_k)最近的顶点(V_{min j})(为了满足必要条件),正因为这一规则[1],我们每研究一个中转点(V_{min j})便可以确定(min orm{overrightarrow{V_kV_{min j}}}),这一步的贪心(剪枝)也造成了该算法的局限性[2]。
【注释[1]】如果存在另一个中转点使得( orm{overrightarrow{V_kV_{min j}}})不是最小,则说明该中转点(V_{TP})与(V_k)的距离小于(V_{min j})与(V_k)的距离,即( orm{overrightarrow{V_kV_{min j}}} gt orm{overrightarrow{V_kV_{TP}}}),这显然与(V_{min j})的定义相矛盾。
【注释[2]】在注释1中我们假设了所有的权值都是正数,当权值出现负数时这个矛盾将不成立,成为Dijkstra算法的局限性之一。
-
算法的描述
-
如何手算做题
- 时间复杂度为(O(N^2)=O( orm{V}^2))(邻接矩阵),(O(log_{2} orm{{V}} imes orm{E}))(堆优化邻接表)。
-
-
Floyd(弗洛伊德)算法
-
解决的问题
可以解决Dijkstra算法不能带负权值的问题,并一口气求了所有的点之间的最短距离。
-
算法的核心
与Dijkstra算法的思想是差不多的,只是Floyd不贪了[1],不再假设所有的权值为正数,所以在确定中转点的时候
不会
去找距离(V_k)最近的点了,而是很野蛮地指定某一个或几个点作为中转点(V_{TP}),将其代入Dijkstra算法的转化条件
:(将每一个i都从1到N遍历一遍,中转点TP也是从1一直累积到N)[small{ orm{overrightarrow{V_kV_i}}^{(TP)}= orm{overrightarrow{V_kV_i}}^{pre(TP)} le orm{overrightarrow{V_kV_{TP}}}^{pre(TP)}+ orm{overrightarrow{V_{TP}V_i}}^{pre(TP)} ? orm{overrightarrow{V_kV_i}} : orm{overrightarrow{V_kV_{TP}}}+ orm{overrightarrow{V_{TP}V_i}}} ]如果中转点可以使得两目标点之间的距离缩小,那就将中转点加入路径集合,否则舍弃中转点并保持不变。这样也能保证局部最优(局部最短路径)。剩下的看看书应该就能明白了。
【注释[1]】Dijkstra算法与Floyd算法的不同就在两者选取中转点的方式不同,剩下的动态规划思想都是一致的。
-
算法的实现
-