美文网首页算法
迪杰斯特拉

迪杰斯特拉

作者: 一凡呀 | 来源:发表于2017-12-05 15:39 被阅读0次

概念:

是从一个顶点到其余各顶点的最短路径的算法,解决的是有向图中最短路径问题。

思路:

自定义数据结构NodeHeap小根堆,小根堆的每个位置上的值是当前结点和到原点距离的封装的一个数据,根据到原点的距离来实现小根堆,NodeHeap里有nodes[],看成堆数组(即数组结构看成堆,数组的下标对应在堆中的位置),heapIndexMap<Node,Integer>用来记录当前结点在堆中的位置,distanceMap(用来记录当前结点和到原点的距离),以及size堆的初始大小。
NodeHeap中有如下方法:
isEmpty()判断堆是否为空;
isEntered()当前结点是否进入过小根堆;
inHeap()当前结点是否还在小根堆中;只有进入过小根堆且,heapIndexMap的index值不是-1时才说明在小根堆,index=-1代表当前结点已经弹出;
heapify()堆排序的下沉过程,其中注意传进来的参数一个是当前结点的下标,另一个是当前堆的大小;
heapInsert()堆排序建立小根堆的过程,这里注意传进来的参数是当前结点和当前结点的小标,比较通过distanceMap.get(nodes[index])来获取当前结点到原点的距离比较;
addOrUpdateOrIgnore()插入节点的过程,如果当前结点没有进入过小根堆就直接加,如果在堆中,就更新小根堆的数据,在这里会出现如下图情况


image.png

如图第一次加数据时B到原点的权值是8,当加入A点后解锁了3的权值路径被解锁,此时原点到B的最小路径是5,所以就要执行更新数据的过程了。
pop()方法,将小根堆堆顶即当前所有节点中到原点距离最小的节点的值弹出,以解锁其它路径等。
总之迪杰斯特拉此实现方法的整体概括是:利用自定义小根堆来简化找最小路径的过程,通过不断弹出已解锁的最小节点路径,来解锁它后序节点,更新最短路径来实现。

以上图为例,解释迪杰斯特拉的过程,最开始是把上图0节点先加入到堆中,遍历他的后序节点A,B,把A,B加入到小根堆,然后因为A的权值小于B,A称为小根堆的新的堆顶,然后解锁A->B权值为3的路径,更新B的最短路径为5,执行结束

代码:

    //自定义节点的结构,一个节点包括的信息是当前节点合到初始节点的距离
    public static class NodeRecord {
        public Node node;
        public int distance;

        public NodeRecord(Node node, int distance) {
            this.node = node;
            this.distance = distance;
        }
    }

    //自定义节点堆小根堆,小根堆上每个节点存储的是 当前结点和当前结点在数组中的下标
    public static class NodeHeap {
        //节点数组
        private Node[] nodes;
        //记录节点是否进入过堆,Node代表当前结点,Integer代表当前结点在堆上的位置
        private HashMap<Node, Integer> heapIndexMap;
        //node代表当前结点,Integer是当前结点到初始结点的距离
        private HashMap<Node, Integer> distanceMap;
        //节点堆的初始大小
        private int size;

        public NodeHeap(int size) {
            //这里的size是用户传来的整个图的节点的个数
            nodes = new Node[size];
            heapIndexMap = new HashMap<>();
            distanceMap = new HashMap<>();
            this.size = 0;
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public void addOrUpdateOrIgnore(Node node, int distance) {
            //如果这个节点进入过堆并且没有弹出就更新他到原点的值
            if (inHeap(node)) {
                distanceMap.put(node, Math.min(distanceMap.get(node), distance));
                //这里注意,insertHeapify的过程是把已经加入的节点和它以前在堆中的位置传进去进行生成小根堆的过程,所以之前的节点信息背覆盖了
                insertHeapify(node, heapIndexMap.get(node));
            }
            //如果这个节点没进入过,就直接分别在数组和堆里同步更新数据
            if (!isEntered(node)) {
                nodes[size] = node;
                heapIndexMap.put(node, size);
                distanceMap.put(node, distance);
                insertHeapify(node, size++);
            }
        }

        public NodeRecord pop() {
            //把距离最小的元素取出来
            NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
            //把头节点和最后一个位置的交换
            swap(0, size - 1);
            //说明当前结点已经弹出
            heapIndexMap.put(nodes[size - 1], -1);
            //移除
            distanceMap.remove(nodes[size - 1]);
            nodes[size - 1] = null;
            heapify(0, --size);
            return nodeRecord;
        }

        //传进来的分别是节点,和节点当前的位置,但是建立是根据到原点的距离建立的
        private void insertHeapify(Node node, int index) {
            while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
                swap(index, (index - 1) / 2);
                index = (index - 1) / 2;
            }
        }

        private void heapify(int index, int size) {
            int left = index * 2 + 1;
            while (left < size) {
                int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
                        ? left + 1 : left;
                smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
                if (smallest == index) {
                    break;
                }
                swap(smallest, index);
                index = smallest;
                left = index * 2 + 1;
            }
        }

        private boolean isEntered(Node node) {
            return heapIndexMap.containsKey(node);
        }

        private boolean inHeap(Node node) {
            return isEntered(node) && heapIndexMap.get(node) != -1;
        }

        //堆和数组同步更新
        private void swap(int index1, int index2) {
            heapIndexMap.put(nodes[index1], index2);
            heapIndexMap.put(nodes[index2], index1);
            Node tmp = nodes[index1];
            nodes[index1] = nodes[index2];
            nodes[index2] = tmp;
        }
    }

    
    public static HashMap<Node, Integer> dijkstra2(Node head, int size) {
        NodeHeap nodeHeap = new NodeHeap(size);
        nodeHeap.addOrUpdateOrIgnore(head, 0);
        HashMap<Node, Integer> result = new HashMap<>();
        while (!nodeHeap.isEmpty()) {
            NodeRecord record = nodeHeap.pop();
            Node cur = record.node;
            int distance = record.distance;
            for (Edge edge : cur.edges) {
                nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
            }
            result.put(cur, distance);
        }
        return result;
    }

相关文章

  • 迪杰斯特拉(Dijkstra)算法描述与路径计算

    1.迪杰斯特拉算法描述 迪杰斯特拉算法是基于LLDP(一种由IEEE802.1AB[11]定义的链路层发现协议)获...

  • 迪杰斯特拉

    概念: 是从一个顶点到其余各顶点的最短路径的算法,解决的是有向图中最短路径问题。 思路: 自定义数据结构NodeH...

  • 迪杰斯特拉

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。它的主要特点是以起始点...

  • 南京地铁最短路径以及最少换乘算法C++不用类

    迪杰斯特拉算法应用于南京地铁求最短路径 深度遍历求图中所有路径 定义的变量名及其作用 然后迪杰斯特拉遍历图是单独放...

  • 迪杰斯特拉(Dijkstra)算法详解

    迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从...

  • 2019-04-21

    迪杰斯特拉算法 #include #include int tu[10][10]; int shorter[10]...

  • 迪杰斯特拉算法

    Java控制台程序,打印的语句很多,直接看控制台输出更好理解。 输出

  • 迪杰斯特拉(Dijkstra)

    迪杰斯特拉算法主要是用广度优先搜索的算法计算出一个顶点V到各个顶点的最短距离ver表示没有走过的顶点,dis表示顶...

  • 弗洛伊德算法(最短路径问题)

    1. 介绍: 弗洛伊德算法和迪杰斯特拉算法一样,都是求最短路径的。迪杰斯特拉算法是求某一个顶点到其他各顶点的最短路...

  • 基本算法——图算法之最短路径(Dijkstra)

    迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余...

网友评论

    本文标题:迪杰斯特拉

    本文链接:https://www.haomeiwen.com/subject/nedcixtx.html