迪瑞克斯拉算法 — 优化

在上一篇迪瑞克斯拉算法中将功能实现了出来,完成了图集中从源点出发获取所有可达的点的最短距离的收集。

但在代码中getMinDistanceAndUnSelectNode()方法的实现并不简洁,每次获取minNode时,都需要遍历整个Map,时间复杂度太高。这篇文章主要是针对上一篇文章代码的一个优化。

其优化的过程中主要是用到了加强堆的数据结构,如果不了解的强烈建议先看加强堆的具体实现。

回顾:

上一篇中,将确定不变的点放入Set中, 每个点之间的距离关系放入Map中,每次遍历Map获取最小minNode,并根据该点找到所有的边,算出最小距离后,放入set中,最终确定最小距离表。

优化

利用加强堆,来维护点和距离的关系,并利用小根堆的优势,让最小的点总是在最上面,需要注意的是,以往的的加强堆中,如果移除了这个元素会直接remove,但是在这里不能remove,因为要记录这个点是否在堆上,是否加入过堆,所以对于确定了的元素,value要改成 -1,用来标记当前元素已经确定,不用再动。

加强堆代码

NodeHeap中的distanceMap用来表示点和距离的关系,根据value的大小动态变化堆顶元素。

主方法是addOrUpdateOrIgnore()。

如果当前元素在堆中并且value != 1(inHeap),说明元素还没有确定,则判断进来的node和value是否大于当前堆中的node对应的value,取小的更新,如果更新,还需要改变元素在堆中位置,因为只可能越更新越小。所以要调用insertHeapify方法去改变堆结构。

如果元素还未加入过堆(!isEntered),则挂在堆尾,并insertHeapify检查是否上移。

pop方法中,如果弹出堆,正常要删除,但是不能删除,除了和最后一个元素交换,下移外,将distanceMap中对应的值改为 -1。否则无法判断该元素是否已经加入过堆,是否已经确定。

java 复制代码
public static class NodeHeap {
        //Node类型的堆
        private Node[] nodes;
        //key对应的Node在堆中的位置是value
        private HashMap<Node, Integer> heapIndexMap;
        //key对应的Node当前距离源点最近距离
        private HashMap<Node, Integer> distanceMap;
        //堆大小
        private int size;

        public NodeHeap(int size) {
            nodes = new Node[size];
            heapIndexMap = new HashMap<>();
            distanceMap = new HashMap<>();
            this.size = 0;
        }

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

        public boolean isEntered(Node head) {
            return heapIndexMap.containsKey(head);
        }

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

        public 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 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;
            }
        }

        public void insertHeapify(Node node, int index) {
            while (distanceMap.get(nodes[index]) < distanceMap.get((index - 1) / 2)) {
                swap(distanceMap.get(nodes[index]), distanceMap.get((index - 1) / 2));
                index = (index - 1) / 2;
            }
        }

        public NodeRecord pop() {
            NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(0));
            swap(0, size - 1);
            heapIndexMap.put(nodes[size - 1], -1);
            distanceMap.remove(nodes[size - 1]);
            heapify(0, --size);
            return nodeRecord;
        }

        public void addOrUpdateOrIgnore(Node node, int distance) {
            if (inHeap(node)) {
                distanceMap.put(node, Math.min(distanceMap.get(node), distance));
                insertHeapify(node, distanceMap.get(node));
            }
            if (!isEntered(node)) {
                nodes[size] = node;
                heapIndexMap.put(node, size);
                distanceMap.put(node, distance);
                insertHeapify(node, size++);
            }
        }
    }

主方法逻辑

上来将给定的点添加到堆中,并且弹出,遍历所有的边放到加强堆中去搞。

java 复制代码
  public static HashMap<Node, Integer> dijkstra2(Node head, int size) {
        NodeHeap nh = new NodeHeap(size);
        nh.addOrUpdateOrIgnore(head, 0);
        HashMap<Node, Integer> result = new HashMap<>();

        while (!nh.isEmpty()) {
            NodeRecord record = nh.pop();
            Node cur = record.node;
            int distance = record.distance;

            for (Edge edge : cur.edges) {
                nh.addOrUpdateOrIgnore(edge.to, distance + edge.weight);
            }
            result.put(cur, distance);
        }
        return result;
    }
相关推荐
chordful2 分钟前
Leetcode热题100-32 最长有效括号
c++·算法·leetcode·动态规划
_OLi_10 分钟前
力扣 LeetCode 459. 重复的子字符串(Day4:字符串)
算法·leetcode·职场和发展·kmp
java小吕布14 分钟前
Java Lambda表达式详解:函数式编程的简洁之道
java·开发语言
Romanticroom17 分钟前
计算机23级数据结构上机实验(第3-4周)
数据结构·算法
白藏y18 分钟前
数据结构——归并排序
数据结构·算法·排序算法
程序员劝退师_21 分钟前
优惠券秒杀的背后原理
java·数据库
ahadee29 分钟前
蓝桥杯每日真题 - 第12天
c++·vscode·算法·蓝桥杯
java小吕布36 分钟前
Java集合框架之Collection集合遍历
java
一二小选手38 分钟前
【Java Web】分页查询
java·开发语言
zhentiya43 分钟前
微积分第五版课后习题答案详解PDF电子版 赵树嫄
算法·pdf