【数据结构】图(五)—— 最短路径算法(二)—— Dijkstra Algorithm(迪杰斯特拉算法)

【数据结构】图(五)—— 最短路径算法(二)—— Dijkstra Algorithm(迪杰斯特拉算法)

两篇不错的博客:
https://www.cnblogs.com/goldsunshine/p/12978305.html
https://www.jianshu.com/p/8b3cdca55dc0

重点细节

计算非加权图中的最短路径,(找出段数最少的路径)可使用 广度优先算法。
计算加权图中的最短路径,(找出最快的路径)可使用 迪杰斯特拉算法 (Dijkstra algorithm)

Dijkstra algorithm 特点

特别重要
  1. 贪心算法思想的一种体现;
  2. 解决赋权有向加权图的单源最短路径问题;
  3. 仅适用于权值为 非负的有向加权图;
  4. 只适用于 有向无环图(directed acyclic graph);

无向图意味着两个节点彼此指向对方,其实就是环;
在无向图中每一条边都是环。

单源最短路径

给定一个带权有向图G=(V,E),其中每条边的权是一个实数。另外,还给定 V 中的一个顶点,称为源。要计算从源到其他所有各顶点的最短路径长度。这里的长度就是指路上各边权之和。这个问题通常称为单源最短路径问题

通俗的讲:就是计算有向图中某一个特定的点(称之为源点),到有向图中其它各点的路径长度。

指定一个点(源点)到其余各个顶点的最短路径,也叫做“单源最短路径

Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。

迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

算法的思路

Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。

然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点,

然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。
然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。

算法步骤

设有向图的顶点为: V V V,设置两个集合 S , T S,T S,T,
S:存放已求出最短路径的顶点的集合;
T: T = V − S T=V-S T=VS, 尚未确定最短路径的顶点集合;

T T T 中顶点按最短路径递增的次序加入到 S S S中,注意每次向 S S S集合中加入一个顶点后,必须对 T T T 中顶点的距离值进行更新。

  1. 初始化:先找出从源点 v 0 v_0 v0 到各终点 v k v_k vk 的直达路径 ( v 0 , v k ) (v_0,v_k) (v0,vk), 即通过一条弧到达的路径;
  2. 从这些路径中找到一条长度最短的路径 ( v 0 , u ) (v_0,u) (v0,u) 并将此顶点加入 S S S 中;
  3. 更新:若在图中存在弧 ( u , v k ) (u,v_k) (u,vk),且 ( v 0 , u ) + ( u , v k ) < ( v 0 , v k ) (v_0,u)+(u,v_k)<(v_0,v_k) (v0,u)+(u,vk)<(v0,vk),则以路径 ( v 0 , u , v k ) (v_0,u,v_k) (v0,u,vk)代替 ( v 0 , v k ) (v_0,v_k) (v0,vk)
  4. 循环操作 2 和 3, 直至 T T T 中集合为 0.

算法图示

推荐视频:https://www.bilibili.com/video/BV1Ut41197ae?from=search&seid=5409160050431847304

视屏讲解十分形象,便于理解
在这里插入图片描述

  1. 第一步, i = 1 i=1 i=1 v 0 v_0 v0到其它各点的距离,如下表可得:到 V 2 V_2 V2的距离最短,将 V 2 V_2 V2存入 S S S 集合;
    在这里插入图片描述

  2. 在更新 S S S 集合的基础上,将 S S S 集合上的点作为可连通的点,更新 V 0 V_0 V0 到其它 T T T 集合的点;选取剩余点中距离最小的点存入 S S S 集合;

在这里插入图片描述
i = 1 i=1 i=1 时,加入了 V 2 V_2 V2 元素,如上图, V 0 V_0 V0 可以通过 V 2 V_2 V2 到其它元素的距离进行更新。结果见下表。
在这里插入图片描述

  1. 循环重复,直至 T T T 集合没有顶点元素

在这里插入图片描述

代码编写

以 python 为例。

一般来说需要三个散列表和一个数组。

在这里插入图片描述

graph:
表示有向图中各顶点与其权值;

costs:
表示起点到其它各顶点的最短距离;

parent:
表示最短路径上某顶点的上一个结点(父结点)

以 下图为例:

在这里插入图片描述

  1. 表示顶点与顶点以及之间的权重关系
graph = {} 
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2

因为设置了 graph[“start”] 为一个散列表,要获取其连通点:

graph["start"].keys()

获取 两顶点之间的权重:

print(graph["start"]["b"])

注意: 设置 graph[“start”] 为一个散列表是十分巧妙的操作,可以有效便捷的实现各项操作;

  1. 创建源点到 T T T 集合中各点的距离表
costs = {}
costs["a"] = 6
costs["b"] = 6
costs["fin"] = float("inf")
  1. 创建存储父节点的散列表
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None

·4. 存放记录过的节点(存放 S S S集合中的元素)

processed = []

注:之所以不需要存放 T T T集合元素的数组,是因为在 costs 散列表中已经体现。

python 代码
# 表示顶点与顶点以及之间的权重关系
graph = {}
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2

graph["a"] = {}
graph["a"]["fin"] = 1

graph["b"] = {}
graph["b"]["fin"] = 5
graph["b"]["a"] = 10
graph["fin"]={}

# 创建源点到 $T$ 集合中各点的距离表
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = float("inf")

# 存储父节点的散列表
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None

# 存放已求出最短路径的结点
processed = [] 

def find_lowest_cost_node(costs):
    '''
    在未处理的结点中,寻找距离最短的一个结点
    :param costs:
    :return: 距离最短的一个结点
    '''
    lowest_cost = float("inf")
    lowest_cost_node = None
    for node in costs:      # 遍历所有结点
        cost = costs[node]
        if cost < lowest_cost and  (node not in processed):
            lowest_cost =  cost # 将其视为距离最低的结点
            lowest_cost_node = node
    return lowest_cost_node

node = find_lowest_cost_node(costs)

while node is not None:
    print("costs",costs)
    cost = costs[node]
    neighbors = graph[node] # 某结点的相邻结点
    print(neighbors.keys())
    for n in neighbors.keys():# 遍历当前节点的所有邻居
        new_cost = cost + neighbors[n]
        if costs[n] > new_cost: # 更新T集合中结点的距离
            costs[n] = new_cost
            parents[n] = node
    processed.append(node)
    node = find_lowest_cost_node(costs)
    print("node:", node)
    #print(graph[node])
print(costs)
java 代码
package Dijkstra;

public class Node {
    private char id;
    private boolean isInTree;
    
    public Node(char id) {
        this.setId(id);
        setInTree(false);
    }

    public char getId() {
        return id;
    }

    public void setId(char id) {
        this.id = id;
    }

    public boolean isInTree() {
        return isInTree;
    }

    public void setInTree(boolean isInTree) {
        this.isInTree = isInTree;
    }
}
package Dijkstra;

public class DistPare {
    private int parentNode;
    private int distance;
    public DistPare(int parentNode, int distance) {
        this.setParentNode(parentNode);
        this.distance = distance;
    }

    public int getParentNode() {
        return parentNode;
    }

    public void setParentNode(int parentNode) {
        this.parentNode = parentNode;
    }
    
    public int getDistance() {
        return distance;
    }
    
    public void setDistance(int distance) {
        this.distance = distance;
    }

}
package Dijkstra;

public class Graph {
    private final int MAXNUM = 20;
    private final int MAXINT = 999999;
    private int nodeNum;       //number of the nodes in Graph
    private int treeNum;       //number of the nodes in tree
    private int adjMatrix[][]; //distance between two nodes
    private Node nodeList[];   //all nodes 
    private DistPare sPath[];  //shortest path between parent node and i  
    private int currentNode;
    private int currentDist;

    public Graph(){
        adjMatrix = new int[MAXNUM][MAXNUM];
        nodeList = new Node[MAXNUM];
        sPath = new DistPare[MAXNUM];
        nodeNum = 0;
        treeNum = 0;
        for(int i=0; i<MAXNUM; i++)
             for(int j=0; j<MAXNUM; j++)
                  adjMatrix[i][j] = MAXINT;
    }
    
    public void addVertex(char id) {
        nodeList[nodeNum++] = new Node(id);
    }
    
    //directed graph
    public void addEdge(int start, int end, int weight) {
        adjMatrix[start][end] = weight;
    }
    
    public void dijkstra() {
        int sourceNode = 0;
        nodeList[sourceNode].setInTree(true);
        treeNum++;
        for(int i = 0; i < nodeNum; i++) {
            int weight = adjMatrix[sourceNode][i];
            sPath[i] = new DistPare(sourceNode, weight);
        }        
        while(treeNum < nodeNum) {
            int minNode = getMin();
            int minDist = sPath[minNode].getDistance();            
            if(minDist == MAXINT) {
                System.out.println("The node can't be reached!");
           }
           else {
                currentNode = minNode;
                currentDist = minDist;
           }
           nodeList[currentNode].setInTree(true);
           treeNum++;
           adjustPath();
        }
        displayPath();
    }
    
    private void adjustPath() {
        // TODO Auto-generated method stub
        int num = 1;
        while(num < nodeNum) {
             if(nodeList[num].isInTree()) {
                  num ++;
                  continue;
             }
             int currentToFringe = adjMatrix[currentNode][num];
             int startToFringe = currentDist + currentToFringe;
             int sPathDist = sPath[num].getDistance();
             if(startToFringe<sPathDist) {
                  sPath[num].setParentNode(currentNode);
                  sPath[num].setDistance(startToFringe);;
             }
             num ++;
        }
    }

    private void displayPath() {
        // TODO Auto-generated method stub
        for(int i=0; i<nodeNum; i++) {
            System.out.print(nodeList[i].getId() + "=");
            if(sPath[i].getDistance() == MAXINT)
                 System.out.print("infinity");
            else
                 System.out.print(sPath[i].getDistance());
            char parent = nodeList[sPath[i].getParentNode()].getId();
            System.out.print("(" + parent + ") ");
       }
       System.out.println(" ");
    }

    /**
     * get the minimum DistPare
     * @return
     */
    private int getMin() {
        int minDist = MAXINT;
        int indexMin = 0;
        for(int j=0; j<nodeNum; j++) {
             if(!nodeList[j].isInTree() && sPath[j].getDistance()<minDist) {
                  minDist = sPath[j].getDistance();
                  indexMin = j;
             }
        }
        return indexMin;
    }
    
}
package Dijkstra;

public class DijkstraTest {
    public static void main(String[] args) {
        Graph theGraph = new Graph();
        theGraph.addVertex('A');//0
        theGraph.addVertex('B');//1
        theGraph.addVertex('C');//2
        theGraph.addVertex('D');//3
        theGraph.addVertex('E');//4

        theGraph.addEdge(0, 1, 50);//AB 50
        theGraph.addEdge(0, 3, 80);//AD 80
        theGraph.addEdge(1, 2, 60);//BC 60
        theGraph.addEdge(1, 3, 90);//BD 90
        theGraph.addEdge(2, 4, 40);//CE 40
        theGraph.addEdge(3, 2, 20);//DC 20
        theGraph.addEdge(3, 4, 70);//DE 70
        theGraph.addEdge(4, 1, 50);//EB 50
       
        System.out.println("Dijkstra: ");
        theGraph.dijkstra();
    }
}

参考代码:

https://www.cnblogs.com/SherryIsMe/p/3480151.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何为xl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值