迪瑞克斯拉算法 — 优化

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

但在代码中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;
    }
相关推荐
短剑重铸之日2 分钟前
《7天学会Redis》Day 6 - 内存&性能调优
java·数据库·redis·缓存·7天学会redis
石头wang3 分钟前
jmeter java.lang.OutOfMemoryError: Java heap space 修改内存大小,指定自己的JDK
java·开发语言·jmeter
咋吃都不胖lyh12 分钟前
Haversine 距离算法详解(零基础友好版)
线性代数·算法·机器学习
FPGA小c鸡17 分钟前
FPGA通信基带算法完全指南:从理论到实战的DSP加速方案
算法·fpga开发
yaoxin52112328 分钟前
292. Java Stream API - 使用构建器模式创建 Stream
java·开发语言
@Aurora.32 分钟前
优选算法【专题三:二分查找算法】
算法
soldierluo32 分钟前
向量与向量数据
人工智能·算法·机器学习
阮松云36 分钟前
code-server 配置maven
java·linux·maven
木木木一40 分钟前
Rust学习记录--C11 编写自动化测试
java·学习·rust
bug总结44 分钟前
uniapp+动态设置顶部导航栏使用详解
java·前端·javascript