首頁 > 軟體

Java利用Dijkstra演演算法求解拓撲關係最短路徑

2022-07-18 18:02:53

演演算法簡介

迪傑斯特拉演演算法(Dijkstra)是由荷蘭電腦科學迪家迪傑斯特拉於1959年提出的,因此又叫狄克斯特拉演演算法。是從一個頂點到其餘各頂點最短路勁演演算法,解決的是有權圖中最短路徑問題。迪傑斯特拉演演算法主要特點是從起始點開始,採用貪婪演演算法的策略,每次遍歷到始點距離最近且未存取過的頂點的鄰接節點,直到擴充套件到終點為止。

程式碼實現思路

1.先初始化源節點(起始點)到其他各個拓撲節點的最短距離,可以用map存放,key為節點,value為節點到源節點的距離。

比如資料庫中儲存的各個拓撲點的資訊,我們需要先把資料庫各地拓撲點之間的距離,載入出來,用map和矩陣(二維陣列)方式。資料庫拓撲資訊儲存表demo:

idsourcetargetdist
1v1v215.67

soure和target為相連的兩個拓撲點,dist是相連線的兩個拓撲點之間的距離。

2.初始化源節點到各個節點之間的距離時,源節點到自身節點的距離設為0,到不相連或者間接相連的節點距離設定為最大。

3.從源節點開始,不斷迴圈迭代,各個節點到源節點的最短路線和距離,更新距離map裡。當迴圈遍歷到目標節點時,即可求出,源節點到目標節點的最短路線和距離。

更多說明,可以看程式碼註釋。

演演算法思想 

求最短路徑步驟 [1] 

演演算法步驟如下: [1] 

G={V,E}

1. 初始時令 S={V0},T=V-S={其餘頂點},T中頂點對應的距離值 [1] 

若存在,d(V0,Vi)為弧上的權值 [1] 

若不存在,d(V0,Vi)為∞ [2] 

2. 從T中選取一個與S中頂點有關聯邊且權值最小的頂點W,加入到S中 [1] 

3. 對其餘T中頂點的距離值進行修改:若加進W作中間頂點,從V0到Vi的距離值縮短,則修改此距離值 [1] 

重複上述步驟2、3,直到S [1]  中包含所有頂點,即W=Vi為止 [1] 

程式碼範例

 
import com.gis.spacedata.domain.entity.tunnel.TunnelTopologyRelEntity;
import lombok.extern.slf4j.Slf4j;
 
import java.util.*;
 
@Slf4j
public class PathUtil {
 
 
    /**
     * 方法描述: 求最短路徑
     *
     */
    public static List<Long> dijkstra(List<TunnelTopologyRelEntity> topologies, long start, long end) {
        int size=topologies.size();
        Map<String, Double> distMap = new HashMap<>(size);
        //存放源節點到各個節點的距離key 目標節點,value 源節點到該節點的距離
        Map<Long, Double> dists = new HashMap<>(size);
        //key: 當前節點,value:從原點到達key的最短路徑的前驅(上一個)節點
        Map<Long, Long> parent = new HashMap<>(size);
        //被標記最短距離的節點
        Set<Long> markNodes = new HashSet<>(size);
        //獲取所有節點列表
        Set<Long> nodes = new HashSet<>(10);
        for (TunnelTopologyRelEntity e : topologies) {
            nodes.add(e.getSource());
            nodes.add(e.getTarget());
            distMap.put(e.getSource() + "-" + e.getTarget(), e.getCost());
        }
        //初始化各個節點到源節點的距離
        for (long node : nodes) {
            if (node == start) {
                dists.put(node, 0d);
            } else {
                dists.put(node, getCost(distMap, start, node));
            }
        }
        // 不斷迭代
        while (true) {
            //距離源節點距離最近的節點(還未被標記為離源節點最近的點)
            long closestNode = -1;
            double min = Double.MAX_VALUE;
            for (Map.Entry<Long, Double> entry : dists.entrySet()) {
                if (entry.getValue() < min && !markNodes.contains(entry.getKey())) {
                    min = entry.getValue();
                    closestNode = entry.getKey();
                }
            }
            // 找不到可達的路徑了或到達目標點
            if (closestNode == -1 || closestNode==end) {
                break;
            }
            markNodes.add(closestNode);
            for (long node : nodes) {
                double dist = getCost(distMap, closestNode, node);
                // 找到一個為擴充套件的子節點
                if (dist > 0 && !markNodes.contains(node)) {
                    double new_dist = dists.get(closestNode) + dist;
                    // 新距離小於原始距離,更新
                    if (new_dist < dists.get(node)) {
                        dists.put(node, new_dist);
                        parent.put(node, closestNode);
                    }
                }
            }
        }
        // 倒敘查詢到路徑
        if (dists.get(end) == Integer.MAX_VALUE) {
            log.info(start + "到" + end + "之間沒有最短路徑");
            return null;
        } else {
            List<Long> path = new ArrayList<>();
            long current = end;
            path.add(current);
            while (current != start) {
                current = parent.get(current);
                path.add(current);
            }
            //反轉
            Collections.reverse(path);
            return path;
        }
    }
 
    /**
     * 方法描述: 獲取相鄰節點之間距離
     *
     */
    private static double getCost(Map<String, Double> distMap, long start, long end) {
        if (start == end) {
            return 0;
        }
        Double dist1 = distMap.get(start + "-" + end);
        if (dist1 != null) {
            return dist1;
        }
        Double dist2 = distMap.get(end + "-" + start);
        if (dist2 != null) {
            return dist2;
        }
        return Double.MAX_VALUE;
    }
 
 
}

實際業務程式碼中應用:

public List<Long> getPointShortWay(String startCode, String endCode) {
        TunnelTopologyCodeRelEntity startTopologyCodeRel = getTopologyCodeRel(startCode);
        TunnelTopologyCodeRelEntity endTopologyCodeRel = getTopologyCodeRel(endCode);
        if (Func.isNull(startTopologyCodeRel) || Func.isNull(endTopologyCodeRel)) {
            return Collections.emptyList();
        }
        List<TunnelTopologyRelEntity> list=list();
        return PathUtil.dijkstra(list,startTopologyCodeRel.getId(), endTopologyCodeRel.getId());
    }

到此這篇關於Java利用Dijkstra演演算法求解拓撲關係最短路徑的文章就介紹到這了,更多相關Java Dijkstra演演算法求解最短路徑內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com