迪瑞克斯拉算法 — 优化

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

但在代码中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;
    }
相关推荐
是苏浙21 小时前
零基础入门Java之认识String类
java·开发语言
岁岁的O泡奶21 小时前
NSSCTF_crypto_[SWPU 2020]happy
经验分享·python·算法·密码学
悟空码字1 天前
从零到一搭建SpringCloud微服务,一场代码世界的“分家”大戏
java·后端·spring cloud
EchoL、1 天前
【论文阅读】SteganoGAN:High Capacity Image Steganography with GANs
论文阅读·人工智能·笔记·算法
CoovallyAIHub1 天前
深度学习驱动的视频异常检测(VAD),AI如何让监控更智能?
深度学习·算法·计算机视觉
于樱花森上飞舞1 天前
【多线程】常见的锁策略与锁
java·开发语言·算法·java-ee
吃喝不愁霸王餐APP开发者1 天前
使用Mockito与WireMock对美团霸王餐接口进行契约测试与集成验证
java·json
明洞日记1 天前
【设计模式手册023】外观模式 - 如何简化复杂系统
java·设计模式·外观模式
独自归家的兔1 天前
面试实录:三大核心问题深度拆解(三级缓存 + 工程规范 + 逻辑思维)
java·后端·面试·职场和发展
HUST1 天前
C 语言 第八讲:VS实用调试技巧
运维·c语言·开发语言·数据结构·算法·c#