最短路徑算法

最短路徑算法通常用在尋找圖中任意兩個(gè)結(jié)點(diǎn)之間的最短路徑或者是求全局最短路徑,像是包括Dijkstra、A*、Bellman-Ford、SPFA(Bellman-Ford的改進(jìn)版本)、Floyd-Warshall、Johnson、BFS等等,這里要集中介紹DijkstraFloyd,前者用來處理任意兩個(gè)結(jié)點(diǎn)之間的最短路徑,后者處理圖中所有的最短路徑。

Dijkstra算法

理解

其思想為,我們首先紀(jì)錄下每個(gè)點(diǎn)到原點(diǎn)的距離,這個(gè)距離會(huì)在每一輪遍歷的過程中刷新。每一個(gè)結(jié)點(diǎn)到原點(diǎn)的最短路徑是其前驅(qū)結(jié)點(diǎn)到原點(diǎn)的最短路徑加上前驅(qū)結(jié)點(diǎn)到當(dāng)前結(jié)點(diǎn)的路徑和。

我們以下面這幅圖片為例,假定起始結(jié)點(diǎn)為1,求該結(jié)點(diǎn)到其余各個(gè)結(jié)點(diǎn)的最短路徑。

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

執(zhí)行步驟為:

步驟描述前驅(qū)結(jié)點(diǎn)
1將當(dāng)前結(jié)點(diǎn)到其它結(jié)點(diǎn)的路徑都初始化為無窮大
2依次計(jì)算每個(gè)結(jié)點(diǎn)的路徑長(zhǎng)度,此時(shí)可以獲得結(jié)點(diǎn)1到其它的結(jié)點(diǎn)2、3、4、5、6的路徑長(zhǎng)度為[7, 9, ∞,∞,14]`,取其中最小的值7,結(jié)點(diǎn)2作為前驅(qū)結(jié)點(diǎn)
3此時(shí)通過結(jié)點(diǎn)2可以到達(dá)結(jié)點(diǎn)4和5,并且其路徑等于起始結(jié)點(diǎn)到前驅(qū)結(jié)點(diǎn)的路徑加上前驅(qū)結(jié)點(diǎn)到目標(biāo)結(jié)點(diǎn)的路徑長(zhǎng)度,因此此時(shí)結(jié)點(diǎn)1到其它結(jié)點(diǎn)3、4、5、6的路徑長(zhǎng)度為[17, 22, ∞, ∞],此時(shí)將現(xiàn)在的路徑長(zhǎng)度和之前的路徑長(zhǎng)度進(jìn)行比較,取對(duì)應(yīng)的較小值,得到最短距離[9, 22, ∞, 14],取其最小值9,結(jié)點(diǎn)3更新成前驅(qū)結(jié)點(diǎn)2
4同樣的道理,此時(shí)結(jié)點(diǎn)1到其它的結(jié)點(diǎn)4、5、6的路徑長(zhǎng)度為[20, ∞, 11],和之前的路徑長(zhǎng)度進(jìn)行比較,得到最短距離[20, ∞, 11],取最小值11,結(jié)點(diǎn)6作為前驅(qū)結(jié)點(diǎn)3
5同樣的道理,得到結(jié)點(diǎn)1到其它的結(jié)點(diǎn)4, 5的路徑長(zhǎng)度為[20, 20],和之前對(duì)比,最短距離更新成[20,20]6
6因?yàn)榇藭r(shí)剩下了兩個(gè)相等路徑長(zhǎng)度的結(jié)點(diǎn),取任意一個(gè)結(jié)點(diǎn),結(jié)果都是一樣的,所以取哪個(gè)都可以

其偽函數(shù)如下:

function Dijkstra(G, w, s)
   for each vertex v in V[G]         # 初始化
         d[v] := infinity             # 將各點(diǎn)的已知最短距離先設(shè)成無窮大
         previous[v] := undefined     # 各點(diǎn)的已知最短路徑上的前趨都未知
   d[s] := 0                         # 因?yàn)槌霭l(fā)點(diǎn)到出發(fā)點(diǎn)間不需移動(dòng)任何距離,所以可以直接將s到s的最小距離設(shè)為0
   S := empty set
   Q := set of all vertices
   while Q is not an empty set       # Dijkstra演算法主體
         u := Extract_Min(Q)
         S.append(u)
         for each edge outgoing from u as (u,v)
                if d[v] > d[u] + w(u,v)       # 拓展邊(u,v)。w(u,v)為從u到v的路徑長(zhǎng)度。
                      d[v] := d[u] + w(u,v)   # 更新路徑長(zhǎng)度到更小的那個(gè)和值。
                      previous[v] := u        # 紀(jì)錄前趨頂點(diǎn)

其中的Extract_Min為提取最小值。

實(shí)現(xiàn)

import sysclass Graph():
    def __init__(self, vertices):
        self.V = vertices
        self.graph = [[0 for _ in range(self.V)] for _ in range(self.V)]
    # 打印距離
    def print_distance(self, dist, parent):
        for node in range(self.V):
            print('distance')
            print(node, dist[node])
            print('path')
            self.print_path(parent, node)
            print('---------')
    # 打印路徑
    def print_path(self, parent, j):
        if parent[j] == -1:
            print(j)
            return
        self.print_path(parent, parent[j])
        print(j)
    # 找出最小距離的結(jié)點(diǎn)
    def min_distance(self, dist, sptSet):
        min_val = sys.maxsize
        min_index = 0
        for v in range(self.V):
            if dist[v] < min_val and not sptSet[v]:
                min_val = dist[v]
                min_index = v
        return min_index
    # 核心算法
    def dijkstra(self, src):
        dist = [sys.maxsize] * self.V
        dist[src] = 0
        sptSet = [False] * self.V
        parent = [-1] * self.V
        for count in range(self.V):
            # 找出前驅(qū)結(jié)點(diǎn)
            u = self.min_distance(dist, sptSet)
            sptSet[u] = True
            # 如果某個(gè)節(jié)點(diǎn)的路徑大于經(jīng)過前驅(qū)結(jié)點(diǎn)的路徑,則更新結(jié)果成經(jīng)過前驅(qū)結(jié)點(diǎn)的路徑
            for v in range(self.V):
                if self.graph[u][v] > 0 and not sptSet[v] and dist[v] > dist[u] + self.graph[u][v]:
                    dist[v] = dist[u] + self.graph[u][v]
                    parent[v] = u
        self.print_distance(dist, parent)g = Graph(8)g.graph = [[0, 0, 7, 7, 2, 0, 0, 0],
           [0, 0, 3, 8, 0, 0, 8, 0],
           [7, 3, 0, 0, 0, 7, 0, 0],
           [7, 8, 0, 0, 0, 0, 0, 0],
           [2, 0, 0, 0, 0, 0, 0, 8],
           [0, 0, 7, 0, 0, 0, 8, 0],
           [0, 8, 0, 0, 0, 8, 0, 6],
           [0, 0, 0, 0, 8, 0, 6, 0]]g.dijkstra(0)

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

Floyd

理解

其原理為:假定存在某個(gè)結(jié)點(diǎn)k在結(jié)點(diǎn)i和結(jié)點(diǎn)j之間,如果i和j之間的路徑長(zhǎng)度大于經(jīng)過k到,則將其距離更新成經(jīng)過結(jié)點(diǎn)k的路徑長(zhǎng)度。因此在實(shí)現(xiàn)中使用三個(gè)循環(huán)即可,但是要注意循環(huán)的順序,最外層是結(jié)點(diǎn)k的遍歷,這樣可以避免i和j的遍歷過早的把最短路徑給定下來。

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

結(jié)果為:

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

實(shí)現(xiàn)

import copyINF = 1e9class Graph():
    def __init__(self, graph):
        self.V = len(graph)
        self.graph = graph
    # 打印距離
    def print_distance(self, dist):
        for i in range(self.V):
            for j in range(self.V):
                print('%5s' % (dist[i][j] if dist[i][j] != INF else 'INF'), end='')
            print('')
    # 核心算法
    def floydwarshall(self):
        dist = copy.deepcopy(self.graph)
        for k in range(self.V):
            for i in range(self.V):
                for j in range(self.V):
                    # 不考慮自己指向自己的情況
                    if i == j:
                        continue
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
        self.print_distance(dist)g = Graph([[INF, INF, 1, INF, INF, INF, INF, INF],
           [INF, INF, 2, 8, INF, 5, INF, INF],
           [9, INF, INF, INF, 2, INF, INF, INF],
           [INF, INF, INF, INF, INF, 2, INF, INF],
           [INF, INF, INF, INF, INF, INF, INF, 9],
           [INF, INF, INF, INF, INF, INF, INF, INF],
           [INF, INF, INF, INF, 1, INF, INF, INF],
           [INF, INF, INF, INF, INF, 7, 7, INF]])g.floydwarshall()

http://www.cnblogs.com/George1994/p/7237473.html