迪瑞克斯拉算法 — 优化

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

但在代码中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;
    }
相关推荐
xuxie9928 分钟前
N11 ARM-irq
java·开发语言
cjy00011134 分钟前
springboot的 nacos 配置获取不到导致启动失败及日志不输出问题
java·spring boot·后端
程序员夏末1 小时前
【LeetCode | 第七篇】算法笔记
笔记·算法·leetcode
wefly20171 小时前
从使用到原理,深度解析m3u8live.cn—— 基于 HLS.js 的 M3U8 在线播放器实现
java·开发语言·前端·javascript·ecmascript·php·m3u8
zhenxin01221 小时前
Spring Boot实现定时任务
java
小江的记录本1 小时前
【事务】Spring Framework核心——事务管理:ACID特性、隔离级别、传播行为、@Transactional底层原理、失效场景
java·数据库·分布式·后端·sql·spring·面试
sheji34162 小时前
【开题答辩全过程】以 基于springboot的校园失物招领系统为例,包含答辩的问题和答案
java·spring boot·后端
寂静or沉默2 小时前
2026最新Java岗位从P5-P7的成长面试进阶资源分享!
java·开发语言·面试
卓怡学长2 小时前
m289在线交友系统
java·spring·tomcat·maven·intellij-idea·hibernate
csdn_aspnet2 小时前
C/C++ 两个凸多边形之间的切线(Tangents between two Convex Polygons)
c语言·c++·算法