Dijkstra 算法说明与实现

作者:Grey

原文地址:

博客园:Dijkstra 算法说明与实现

CSDN:Dijkstra 算法说明与实现

问题描述

问题:给定出发点,出发点到所有点的距离之和最小是多少?

注:Dijkstra 算法必须指定一个源点,每个边的权值均为非负数,求这个点到其他所有点的最短距离,到不了则为正无穷, 不能有累加和为负数的环。

题目链接见:LeetCode 743. Network Delay Time

主要思路

  1. 生成一个源点到各个点的最小距离表,一开始只有一条记录,即原点到自己的最小距离为0, 源点到其他所有点的最小距离都为正无穷大

  2. 从距离表中拿出没拿过记录里的最小记录,通过这个点发出的边,更新源 点到各个点的最小距离表,不断重复这一步

  3. 源点到所有的点记录如果都被拿过一遍,过程停止,最小距离表得到了。

关键优化:加强堆结构说明

完整代码见:

class Solution {
public static int networkDelayTime(int[][] times, int N, int K) {
Graph graph = generate(times);
Node from = null;
for (Node n : graph.nodes.values()) {
if (n.value == K) {
from = n;
}
}
HashMap<Node, Integer> map = dijkstra2(from, N);
int sum = -1; for (Map.Entry<Node, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
N--;
continue;
}
N--;
if (entry.getValue() == Integer.MAX_VALUE) {
return -1;
} else {
sum = Math.max(entry.getValue(), sum);
}
}
// 防止出现环的形状
// int[][] times = new int[][]{{1, 2, 1}, {2, 3, 2}, {1, 3, 1}};
// int N = 3;
// int K = 2;
if (N != 0) {
return -1;
}
return sum;
} public static Graph generate(int[][] times) {
Graph graph = new Graph();
for (int[] time : times) {
int from = time[0];
int to = time[1];
int weight = time[2];
if (!graph.nodes.containsKey(from)) {
graph.nodes.put(from, new Node(from));
}
if (!graph.nodes.containsKey(to)) {
graph.nodes.put(to, new Node(to));
}
Node fromNode = graph.nodes.get(from);
Node toNode = graph.nodes.get(to);
Edge fromToEdge = new Edge(weight, fromNode, toNode);
//Edge toFromEdge = new Edge(weight, toNode, fromNode);
fromNode.nexts.add(toNode);
fromNode.out++;
//fromNode.in++;
//toNode.out++;
toNode.in++;
fromNode.edges.add(fromToEdge);
//toNode.edges.add(toFromEdge);
graph.edges.add(fromToEdge);
//graph.edges.add(toFromEdge);
} return graph;
} public static class Graph {
public HashMap<Integer, Node> nodes;
public HashSet<Edge> edges; public Graph() {
nodes = new HashMap<>();
edges = new HashSet<>();
}
} public static class Edge {
public int weight;
public Node from;
public Node to; public Edge(int weight, Node from, Node to) {
this.weight = weight;
this.from = from;
this.to = to;
}
} public static class Node {
public int value;
public int in;
public int out;
public ArrayList<Node> nexts;
public ArrayList<Edge> edges; public Node(int value) {
this.value = value;
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
} public static Node getMinNode(HashMap<Node, Integer> distanceMap, HashSet<Node> selectedNodes) {
int minDistance = Integer.MAX_VALUE;
Node minNode = null;
for (Map.Entry<Node, Integer> entry : distanceMap.entrySet()) {
Node n = entry.getKey();
int distance = entry.getValue();
if (!selectedNodes.contains(n) && distance < minDistance) {
minDistance = distance;
minNode = n;
}
}
return minNode;
} 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; // 实际的堆结构
// key 某一个node, value 上面堆中的位置
private HashMap<Node, Integer> heapIndexMap;
// key 某一个节点, value 从源节点出发到该节点的目前最小距离
private HashMap<Node, Integer> distanceMap;
private int size; // 堆上有多少个点 public NodeHeap(int size) {
nodes = new Node[size];
heapIndexMap = new HashMap<>();
distanceMap = new HashMap<>();
size = 0;
} public boolean isEmpty() {
return size == 0;
} // 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance
// 判断要不要更新,如果需要的话,就更新
public void addOrUpdateOrIgnore(Node node, int distance) {
if (inHeap(node)) {
distanceMap.put(node, Math.min(distanceMap.get(node), distance));
insertHeapify(node, heapIndexMap.get(node));
}
if (!isEntered(node)) {
nodes[size] = node;
heapIndexMap.put(node, size);
distanceMap.put(node, distance);
insertHeapify(node, size++);
}
} public NodeRecord pop() {
NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
swap(0, size - 1);
heapIndexMap.put(nodes[size - 1], -1);
distanceMap.remove(nodes[size - 1]);
// free C++同学还要把原本堆顶节点析构,对java同学不必
nodes[size - 1] = null;
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;
}
} private boolean isEntered(Node node) {
return heapIndexMap.containsKey(node);
} 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;
}
} // 改进后的dijkstra算法
// 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回
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;
for (Edge edge : cur.edges) {
nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
}
result.put(cur, distance);
}
return result;
}
}

代码说明:本题未采用题目给的二维数组的图结构,而是把二维数组转换成自己熟悉的图结构,再进行dijkstra算法。

更多

算法和数据结构笔记

参考资料

算法和数据结构体系班-左程云

Dijkstra 算法说明与实现的更多相关文章

  1. 求两点之间最短路径-Dijkstra算法

     Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.D ...

  2. Dijkstra算法优先队列实现与Bellman_Ford队列实现的理解

    /* Dijkstra算法用优先队列来实现,实现了每一条边最多遍历一次. 要知道,我们从队列头部找到的都是到 已经"建好树"的最短距离以及该节点编号, 并由该节点去更新 树根 到其 ...

  3. 关于dijkstra算法的一点理解

    最近在准备ccf,各种补算法,图的算法基本差不多看了一遍.今天看的是Dijkstra算法,这个算法有点难理解,如果不深入想的话想要搞明白还是不容易的.弄了一个晚自习,先看书大致明白了原理,就根据书上的 ...

  4. 最短路模板(Dijkstra & Dijkstra算法+堆优化 & bellman_ford & 单源最短路SPFA)

    关于几个的区别和联系:http://www.cnblogs.com/zswbky/p/5432353.html d.每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个(草儿家到 ...

  5. Dijkstra算法(二)之 C++详解

    本章是迪杰斯特拉算法的C++实现. 目录 1. 迪杰斯特拉算法介绍 2. 迪杰斯特拉算法图解 3. 迪杰斯特拉算法的代码说明 4. 迪杰斯特拉算法的源码 转载请注明出处:http://www.cnbl ...

  6. Dijkstra算法(一)之 C语言详解

    本章介绍迪杰斯特拉算法.和以往一样,本文会先对迪杰斯特拉算法的理论论知识进行介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现. 目录 1. 迪杰斯特拉算法介绍 2. 迪杰斯特拉算法 ...

  7. 最短路问题Dijkstra算法

    Dijkstra算法可以解决源点到任意点的最短距离并输出最短路径 准备: 建立一个距离数组d[ n ],记录每个点到源点的距离是多少 建立一个访问数组v[ n ],记录每个点是否被访问到 建立一个祖先 ...

  8. dijkstra算法求最短路

    艾兹格·W·迪科斯彻 (Edsger Wybe Dijkstra,1930年5月11日~2002年8月6日)荷兰人. 计算机科学家,毕业就职于荷兰Leiden大学,早年钻研物理及数学,而后转为计算学. ...

  9. 数据结构之Dijkstra算法

    基本思想 通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算). 此外,引进两个集合S和U.S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求 ...

  10. ACM: HDU 1869 六度分离-Dijkstra算法

    HDU 1869六度分离 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Descri ...

随机推荐

  1. Filebeat 调试

    默认情况下,Filebeat将其所有输出发送到syslog. 在前台运行Filebeat时,可以使用-e命令行标志将输出重定向到标准错误. 例如: filebeat -e 默认配置文件是filebea ...

  2. Grafana 入门知识介绍

    通过[Configuration]>[Plugins]添加插件 通过[Configuration]>[Data Sources]添加数据源(分析对象) 通过[Server Admin]&g ...

  3. linux安装node和达梦数据库8

    PS.本次测试只是为了项目需要,但是在部署和启动程序的时候发生了一系列的报错,由此记录下来为日后作参考 安装达梦数据库 1. 达梦数据库(DM8)简介 达梦数据库管理系统是武汉达梦公司推出的具有完全自 ...

  4. P7361 「JZOI-1」拜神 (字符串)

    题意: 给一个串,\(Q\) 次询问区间 \([l,r]\) 中至少出现两次的子串的最大长度. 写LCT是什么东东 以下做法很经典: 先求出 SA 以及 height 数组,然后按 height 从大 ...

  5. 使用SqlDataReader对象从数据库中检索只读的数据。

    SqlDataReader对象每次从查询结果中读取一行到内存中,对于sql数据库,如果只需要顺序读取,可以优先选择SqlDataReader,其对数据库的读取速度非常快. 调用SqlDataReade ...

  6. P1706 全排列问题 方法记录

    原题链接 全排列问题 题目描述 按照字典序输出自然数 \(1\) 到 \(n\) 所有不重复的排列,即 \(n\) 的全排列,要求所产生的任一数字序列中不允许出现重复的数字. 输入格式 一个整数 \( ...

  7. C++ 栈和典型迷宫问题

    C++ 栈和迷宫问题 1. 前言 栈是一种受限的数据结构,要求在存储数据时遵循先进后出(Last In First Out)的原则.可以把栈看成只有一个口子的桶子,进和出都是走的这个口子(也称为栈顶) ...

  8. 魔改xxl-job,彻底告别手动配置任务!

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是Hydra. xxl-job是一款非常优秀的任务调度中间件,轻量级.使用简单.支持分布式等优点,让它广泛应用在我们的项目中,解 ...

  9. DDD-领域驱动(四)-使用IMediator 实现领域事件

    领域事件是指:一个领域中出触发的 集成事件是指:多个微服务之前产生的事件 核心对象 IMediator INotification INotificationHandler 引入:IMediator ...

  10. .NET 采用 SkiaSharp 生成二维码和图形验证码及图片进行指定区域截取方法实现

    在最新版的 .NET 平台中,微软在逐步放弃 System.Drawing.Imaging ,给出的理由如下: System.Drawing命名空间对某些操作系统和应用程序类型有一些限制. 在Wind ...