【路径规划】Dijkstra算法——超详细原理图解

Dijkstra算法详解
  • 1. Dijkstra算法原理
    •  1.1. 有向图的Dijkstra算法
    •  1.2. 无向图和栅格网络的拓展
      •   1.2.1. 无向图
      •   1.2.2. 栅格网络
  • 2. Dijkstra程序实现
    •  Java

1. Dijkstra算法原理

 1.1. 有向图的Dijkstra算法

  网上关于Dijkstra算法的文章纷繁复杂,有的在算法流程上有一些问题或谬误,有的并没有明确解释算法的详细流程,有的只介绍了简单的流程步骤,没有后续迭代的步骤。所以我决定将该算法的彻底完整流程以图表配文的形式详细描述一遍,希望能给有需要的人带来帮助。
  Dijkstra算法在最短路径问题上有着十分稳定、准确的最短路径搜索结果,是十分经典的路径规划算法,其基础理论也作为许多最短路径算法的基础,那么我们就来图解一下Dijkstra算法的具体流程:
  以下图5节点带权边的有向图为例:
Dijkstra1

openABCDE
closed

  A为路径起点,E为路径终点,每个节点含2个信息,第1个是在从起点到该节点的当前最短路径上,该节点的父节点,初始化为自身;第2个是从起点到该节点的当前最短路径长度。
  同时算法要维护两个集合,open开集集合和closed闭集集合,open开集集合存储还未确定到达起点的最短路径的节点,初始化包含所有点集,closed闭集集合存储已经确定了到达起点的最短路径的节点,初始化为空集合。
  第一步:
Dijsktra2

openBCDE
closedA

  1)将起点A从open集合中去除并加入closed集合。
  2)计算并更新在open集合中起点A可达的节点B、C、D到起点A的距离,并更新这些节点的父节点为A,并将剩余节点E的父节点置为空或自身皆可,将其距离值置为正无穷大或一个足够大的数。
  第二步:
Dijsktra3

openBDE
closedAC

  1)选取open集合中距离起点A的最短路径长度最小的节点C,将其从open集合中去除并加入closed集合中。
  第三步:
Dijsktra4

openBDE
closedAC

  1)从上一步选出的节点C出发,计算所有其可达的并在open集合中的节点B,更新B的最短路径长度和父节点,当前B的最短路径距离为9,父节点为A,而若从C出发到达B,则最短路径长度将减小为7,符合更新条件,所以将B的最短路径长度更新为7,其父节点更新为C。
  第四步:
Dijkstra5

openBE
closedACD

  1)选取open集合中距离起点A的最短路径长度最小的节点D,将其从open集合中去除并加入closed集合中。
  第五步:
Dijkstra6

openBE
closedACD

  1)从上一步选出的节点D出发,计算所有其可达的并在open集合中的节点E,由于当前E还未更新最短路径长度和父节点,所以直接更新E的最短路径长度和父节点为10和D。
  第六步:
Dijkstra7

openE
closedABCD

  1)选取open集合中距离起点A的最短路径长度最小的节点B,将其从open集合中去除并加入closed集合中。
  第七步:
Dijkstra8

openE
closedABCD

  1)从上一步选出的节点B出发,计算所有其可达的并在open集合中的节点E,当前E节点的最短路径长度为10,其父节点为D,而A经过B到达E的话,其路径长度为12,大于E原有的最短路径长度,不符合更新条件,所以对E的更新失败,E仍然保留原有的最短路径长度和父节点。
  第八步:
Dijkstra9

open
closedABCDE

  1)选取open集合中距离起点A的最短路径长度最小的节点E,将其从open集合中去除并加入closed集合中。
  第九步:
Dijkstra10

open
closedABCDE

  1)由于目标点E已经被加入closed闭集合中,所以算法迭代中止,已经找到了从A到E的最短路径。
  2)从路径终点E开始,根据父节点逆推路径可以得到,从A到E的最短路径是A-D-E,其最短路径长度为10。
  至此,带权边的有向图中单源最短路径已经找到,Dijkstra算法的整个运行流程也便是如此。相信这样的图解已经十分详细表现了Dijkstra算法的运行流程啦。

 1.2. 无向图和栅格网络的拓展

  1.2.1. 无向图

  无向图的Dijkstra算法只需要在上述有向图的算法中进行拓展,算法差别只是在每一次从选定节点向外搜索可达节点并更新的环节上,无向图中可以选择所有open集合中与选定节点相连的节点,可拿上述过程的第二步和第三步做直观比较:
  “第二步:”
Dij1
  “第三步:”
Dij2
  面向无向图的拓展改进是好理解并好实现的,而在机器路径规划时常用到的栅格网络,则是无向图的一种特例,是一种规则化等权边的无向图。

  1.2.2. 栅格网络

  栅格网络常用在对机械运动的构型空间的建模中,用于进行路径规划,而栅格网络可以理解成规则排布的一个个节点,每个连边的权重为1:
在这里插入图片描述
  利用这样的等效,将自由空间节点和障碍物节点根据空间情况进行划分和构建,就可以使用Dijkstra算法在栅格化的构型空间中寻找出机械运作或移动的最短路径。

2. Dijkstra程序实现

  程序范例以上文提到的例子进行测试:
demo1
根据该图,可以创建输入数据文本:
demo2

 Java

  代码:

//Dijkstra算法
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

public class Main {
    public static void main(String[] args) {
        String fileName = "demo.txt";
        Character startPoint = 'A';
        Character endPoint = 'E';
        File file = new File(fileName);
        BufferedReader reader = null;
        String tempString = null;
        HashMap<Character, ArrayList<TwoTuple<Character, Integer>>> connects = new HashMap<>();
        try {
            reader = new BufferedReader(new FileReader(file));
            while (null != (tempString = reader.readLine())) {
                String[] tempStrSplit = tempString.split(" ");
                TwoTuple<Character, Integer> tempTwoTuple = new TwoTuple<>(tempStrSplit[1].charAt(0), Integer.parseInt(tempStrSplit[2]));
                if (connects.containsKey(tempStrSplit[0].charAt(0))) { //连接集合已经含有该点出边
                    ArrayList<TwoTuple<Character, Integer>> tempArr = connects.get(tempStrSplit[0].charAt(0));
                    tempArr.add(tempTwoTuple);
                    connects.replace(tempStrSplit[0].charAt(0), tempArr);
                } else { //连接集合未含该点出边
                    ArrayList<TwoTuple<Character, Integer>> tempArr = new ArrayList<>();
                    tempArr.add(tempTwoTuple);
                    connects.put(tempStrSplit[0].charAt(0), tempArr);
                }
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        TwoTuple<Character[], Integer> res = DijkstraMethod(connects, startPoint, endPoint); //获得最短路径结果
        System.out.println("Path :"); //打印最短路径结果
        for (int i = 0; i < res.fst.length - 1; i++) {
            System.out.print(res.fst[i] + " -> ");
        }
        System.out.println(res.fst[res.fst.length - 1]);
        System.out.println("Length :");
        System.out.println(res.snd);
    }
    public static TwoTuple<Character[], Integer> DijkstraMethod(HashMap<Character, ArrayList<TwoTuple<Character, Integer>>> source, Character startPoint, Character endPoint) {
        ArrayList<Character> open = new ArrayList<>();
        ArrayList<Character> closed = new ArrayList<>();
        HashSet<Character> points = new HashSet<>( );
        source.forEach((k, v) -> { //获得所有点集
            points.add(k);
            v.forEach(x -> {
                points.add(x.fst);
            });
        });
        Character[] allPoints = new Character[points.size()];
        Character[] fatherPoints = new Character[points.size()];
        int[] dists = new int[points.size()];
        int[] distRes = new int[points.size()];
        int k = 0;
        for (Iterator<Character> iterator = points.iterator(); iterator.hasNext(); ) { //将点集转化为字符数组形式
            Character thisChar = iterator.next();
            open.add(thisChar); //添加开集
            allPoints[k] = thisChar; //初始化节点数组
            fatherPoints[k] = thisChar; //初始化父节点数组
            dists[k] = Integer.MAX_VALUE; //初始化距离数组
            k++;
        }
        k = findIndex(allPoints, startPoint);
        dists[k] = 0;
        int tempIndex = 0;
        while (open.contains(endPoint)) { //开集有终点则一直继续寻找轨迹
            k = findMinNumIndex(dists); //找到当前距离最小节点
            open.remove(allPoints[k]); //从开集中移除
            closed.add(allPoints[k]); //加入闭集
            distRes[k] = dists[k]; //将最终距离存储
            dists[k] = -1; //距离置为-1,不参与最小值判断
            if (allPoints[k].equals(endPoint)) { //若终点被移除,则停止迭代
                break;
            }
            ArrayList<TwoTuple<Character, Integer>> thisPointConnect = source.get(allPoints[k]);
            for (int i = 0; i < thisPointConnect.size(); i++) {
                tempIndex = findIndex(allPoints, thisPointConnect.get(i).fst);
                if (distRes[k] + thisPointConnect.get(i).snd < dists[tempIndex]) { //符合更新条件
                    fatherPoints[tempIndex] = allPoints[k];
                    dists[tempIndex] = distRes[k] + thisPointConnect.get(i).snd;
                }
            }
        }
        //输出路径和距离
        k = findIndex(allPoints, endPoint);
        ArrayList<Character> output = new ArrayList<>();
        output.add(endPoint);
        while (!allPoints[k].equals(startPoint)) {
            output.add(fatherPoints[k]);
            k = findIndex(allPoints, fatherPoints[k]);
        }
        Character[] outChars = new Character[output.size()];
        for (int i = 0; i < output.size(); i++) {
            outChars[i] = output.get(output.size() - 1 - i);
        }
        k = findIndex(allPoints, endPoint);
        TwoTuple<Character[], Integer> res = new TwoTuple<>(outChars, distRes[k]);
        return res;
    }
    public static int findIndex(Character[] chars, Character charOne) {
        for (int i = 0; i < chars.length; i++) {
            if (chars[i].equals(charOne)) {
                return i;
            }
        }
        return -1;
    }
    public static int findMinNumIndex(int[] ints) {
        int minNum = Integer.MAX_VALUE;
        int minNumIndex = 0;
        for (int i = 0; i < ints.length; i++) {
            if (ints[i] < minNum && ints[i] >= 0) {
                minNum = ints[i];
                minNumIndex = i;
            }
        }
        return minNumIndex;
    }
}
class TwoTuple<K, V> {
    public final K fst;
    public final V snd;
    TwoTuple(K k, V v){
        fst = k;
        snd = v;
    }
}

  结果:
Res

【路径规划】Dijkstra算法——超详细原理图解_dijkstra算法过程图解-CSDN博客

Dijkstra算法是一种用于在加权图中找到最短路径的算法,它可以处理没有负权边的图。算法的基本思想是,从源点开始,逐步增加到其他顶点的距离,直到找到最短路径为止。Dijkstra算法采用贪心策略,每次找到距离源点最近的一个未被访问的顶点,并更新其他顶点到源点的距离。 在Java中实现Dijkstra算法通常需要使用优先队列来优化查找当前距离源点最近顶点的过程。以下是Dijkstra算法的Java实现的一个简单例子: ```java import java.util.*; class Dijkstra { // 图的顶点数量 private static final int N = 9; // Dijkstra算法的实现 public static void dijkstra(int[][] graph, int startVertex) { // 记录源点到每个顶点的最短路径 int[] dist = new int[N]; // 初始化距离数组,所有顶点距离设置为无穷大 Arrays.fill(dist, Integer.MAX_VALUE); // 用于标记顶点是否被访问过 boolean[] visited = new boolean[N]; // 起始点到自身的距离是0 dist[startVertex] = 0; // 用优先队列优化查找最小距离的顶点 PriorityQueue<Integer> pq = new PriorityQueue<>(Comparator.comparingInt(i -> dist[i])); // 将起始点加入优先队列 pq.add(startVertex); while (!pq.isEmpty()) { // 从优先队列中选出距离最小的顶点 int u = pq.poll(); // 如果这个顶点已经被访问过,跳过 if (visited[u]) continue; // 标记顶点为已访问 visited[u] = true; // 遍历所有邻接的顶点 for (int v = 0; v < N; v++) { // 如果顶点u到顶点v存在边,并且顶点v未被访问 if (graph[u][v] != 0 && !visited[v]) { // 计算源点通过顶点u到顶点v的路径长度 int newDist = dist[u] + graph[u][v]; // 如果新的路径长度小于当前记录的路径长度,则更新之 if (newDist < dist[v]) { dist[v] = newDist; // 将顶点v加入优先队列 pq.add(v); } } } } // 输出从源点到每个顶点的最短路径长度 for (int i = 0; i < N; i++) { System.out.println("Distance from vertex " + startVertex + " to vertex " + i + " is " + dist[i]); } } public static void main(String[] args) { // 示例图的邻接矩阵表示 int[][] graph = { {0, 4, 0, 0, 0, 0, 0, 8, 0}, {4, 0, 8, 0, 0, 0, 0, 11, 0}, {0, 8, 0, 7, 0, 4, 0, 0, 2}, {0, 0, 7, 0, 9, 14, 0, 0, 0}, {0, 0, 0, 9, 0, 10, 0, 0, 0}, {0, 0, 4, 14, 10, 0, 2, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 1, 6}, {8, 11, 0, 0, 0, 0, 1, 0, 7}, {0, 0, 2, 0, 0, 0, 6, 7, 0} }; // 从顶点0开始计算最短路径 dijkstra(graph, 0); } } ``` 在这个例子中,`graph`是一个图的邻接矩阵表示,`dijkstra`方法实现了Dijkstra算法,`main`方法用于测试算法。请注意,这个例子假设图是用邻接矩阵表示的,并且顶点编号从0开始。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值