zoukankan      html  css  js  c++  java
  • DiJkstra(狄克斯特拉)算法

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

    迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

    要求:图中不能有累加和为负数的环

    思路:

      代码: 

    package Algorithms.Graph;
    
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map.Entry;
    
    public class Dijkstra {
        //获取从head出发到所有点的最小距离
        public static HashMap<Node, Integer> dijkstra1(Node head) {
            //所有的距离指的是从唯一的源头(head)到当前点的最小距离
            // key:从head出发到达key
            // value:从head出发到达key的最小距离
            //如果在表中没有T的记录,含义是从head出发到T这个点的距离为正无穷
            HashMap<Node, Integer> distanceMap = new HashMap<>();
            distanceMap.put(head, 0);//把从head到head的最小距离0加入distanceMap中
            //selectedNodes用于存放已经求过距离的节点,以后再不不碰
            HashSet<Node> selectedNodes = new HashSet<>();
    
            //得到一个距离最小的点A
            Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
            while (minNode != null) {
                int distance = distanceMap.get(minNode); //计算最小距离
                for (Edge edge : minNode.edges) { //循环点A的所有边
                    Node toNode = edge.to; //边所对应的点X
                    if (!distanceMap.containsKey(toNode)) { //如果点X不在distanceMap中
                        distanceMap.put(toNode, distance + edge.weight); //在distanceMap中记录从head到X点的距离
                    } else {
                        //如果X在distanceMap中,更新最小距离
                        distanceMap.put(toNode, Math.min(distanceMap.get(toNode), distance + edge.weight));
                    }
                }
                selectedNodes.add(minNode); //把点A添加到已经求过距离的节点
                //再次得到distanceMap中距离最小的节点B,然后重复以上操作,
                minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
                // 最后所有的点都会进入selectedNode,minNode就会为null,退出循环,返回distanceMap
            }
            return distanceMap;
        }
    
        //在distanceMap选择最小距离的节点,但不能是已经选过的点selectedNode
        public static Node getMinDistanceAndUnselectedNode(HashMap<Node, Integer> distanceMap,
                                                           HashSet<Node> selectedNode) {
            Node minNode = null;
            int minDistance = Integer.MAX_VALUE; //到每点距离默认为系统最大值
            for (Entry<Node, Integer> entry : distanceMap.entrySet()) { //遍历distanceMap
                Node node = entry.getKey(); //取当前节点
                int distance = entry.getValue(); //取当前节点所对应的距离
                //如果这个Node不在已经求过节点的集合中且这个距离比当前最小距离小
                if (!selectedNode.contains(node) && distance < minDistance) {
                    minNode = node; //更新最小距离的节点
                    minDistance = distance;  //更新最小距离
                }
            }
            return minNode;
        }   
    }  
    package Algorithms.Graph;
    
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map.Entry;
    
    public class Dijkstra {
        //head到节点的最短距离
        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; //储存节点的数组(堆的底层结构是数组)
            private HashMap<Node, Integer> heapIndexMap; //value:任一节点在堆上的Index。通过Node查它在不在堆上,如果在,拿到它在堆上的位置
            private HashMap<Node, Integer> distanceMap; //head到Node目前最短的距离
            private int size; //堆上的节点
    
            //初始化
            public NodeHeap(int size) {
                nodes = new Node[size];
                heapIndexMap = new HashMap<>();
                distanceMap = new HashMap<>();
                this.size = 0;
            }
    
            public boolean isEmpty() {
                return size == 0;
            }
    
            //塞一条记录,如果从head出发 ,到某一个节点的记录第一次出现,增加此记录
            //如果塞的记录之前有过,但这个值比记录的更小,更新此记录;如果比记录值大,忽略
            public void addOrUpdateOrIgnore(Node node, int distance) {
                if (inHeap(node)) { //如果Node在堆上
                    distanceMap.put(node, Math.min(distanceMap.get(node), distance)); //更新
                    insertHeapify(node, heapIndexMap.get(node));//更新之后,可能经历一个往上冒的过程
                }
                if (!isEntered(node)) { //如果Node节点没有进来过
                    nodes[size] = node;
                    heapIndexMap.put(node, size);
                    distanceMap.put(node, distance);
                    insertHeapify(node, size++);
                }
            }
    
            //弹出一个自己定义的Node
            public NodeRecord pop() {
                NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
                swap(0, size - 1); //把堆的最后一个元素拿到堆顶
                heapIndexMap.put(nodes[size - 1], -1); //把原本的头节点指向-1位置
                distanceMap.remove(nodes[size - 1]); //distanceMap中删掉此条记录
                nodes[size - 1] = null; //在堆上释放size-1位置
                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;
                }
            }
    
            //Node进没进来过
            private boolean isEntered(Node node) {
                return heapIndexMap.containsKey(node);
            }
    
            //判断一个Node在不在堆上,首先得进来过,其次Node在heapIndexMap上的位置不是-1表示在堆上
            //-1表示曾经进来过,但不在堆上
            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;
            }
        }
    
        /**
         * 改进后的dijkstrs算法
         * 从head出发,所有head能到达的节点,生成到达每个节点的最小路径并返回
         * @param head :头节点
         * @param size :节点数量
         * @return :HashMap<Node, Integer>   Node:能够到达的节点   Interger:头节点到此节点的最短距离
         */
        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;
                //找出当前节点所有的下一个节点,进行addOrUpdateOrIgnore
                for (Edge edge : cur.edges) {
                    nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
                }
                result.put(cur, distance); //加一条记录
            }
            return result;
        }
    }
    优化(自定义堆结构)
  • 相关阅读:
    【BZOJ4337】[BJOI2015] 树的同构(哈希水题)
    【BZOJ4176】Lucas的数论(杜教筛)
    【BZOJ2627】JZPKIL(数论大杂烩)
    【BZOJ2228】[ZJOI2011] 礼物(巧妙的三部曲)
    【BZOJ2954】[POI2002] 超级马(暴搜)
    【BZOJ4498】魔法的碰撞(动态规划)
    【BZOJ3489】A simple rmq problem(三维数点)
    【BZOJ2626】JZPFAR(KD-Tree)
    【BZOJ4520】[CQOI2016] K远点对(KD-Tree)
    【BZOJ1941】[SDOI2010] Hide and Seek(KD-Tree)
  • 原文地址:https://www.cnblogs.com/zh-xiaoyuan/p/15139212.html
Copyright © 2011-2022 走看看