当青训营遇上码上掘金
前言
本人做题思路借鉴了文章当青训营遇上码上掘金之”寻友之旅“ - 掘金 (juejin.cn),我对算法无过多了解,目前仅知道代码的性能还有很大的优化空间,也恳请各位能给出一些优化的建议。
如有错误或疏漏,还望海涵。
题目
主题 3:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
思路
很明显这是一个求最短路径的问题,目前本人了解到的两种相关算法分别是迪杰斯特拉算法和佛洛依德算法,由于需要求单源最短路径,所以该题目采用前者作为解决方案。
另外,还需要使用到一个名为“小根堆”的数据结构,它是一棵二叉树,这棵树的子节点不大于它的父节点。
最后,还需要设置一个备忘录,将已经确认过最短路的节点放进去,下一次就不需要再遍历这个节点了。
步骤如下:
- 将小青的位置(起点)加入到堆中,同时设置该点的距离为0(它本身)。
- 取出堆顶节点。
- 遍历堆顶节点的邻接点(n-1、n+1、n*2),如果它们不在规定的范围内或者已经确认过,跳过,否则对于当前堆顶节点的每一个相邻节点来说,如果通过当前的堆顶节点到达这些相邻节点的路径比之前所得路径还短,那么就进行更新。
- 重复上述两步,直到堆为空。
实现
import java.util.*;
class Dijkstra {
public static int dijkstra(int n, int k) {
final int max = 100000;
int[] distances = new int[max + 1];
Arrays.fill(distances, Integer.MAX_VALUE);
distances[n] = 0;
Set<Integer> processed = new HashSet<>();
PriorityQueue<Node> heap = new PriorityQueue<>(new Comparator<Node>() {
public int compare(Node n1, Node n2) {
return n1.distance - n2.distance;
}
});
heap.offer(new Node(0, n));
while (!heap.isEmpty()) {
Node node = heap.poll();
int distance = node.distance;
int curr = node.index;
if (processed.contains(curr)) {
continue;
}
processed.add(curr);
int[] neighbors = new int[]{curr - 1, curr + 1, curr * 2};
for (int neighbor : neighbors) {
if (neighbor < 0 || neighbor > max || processed.contains(neighbor)) {
continue;
}
if (distance + 1 < distances[neighbor]) {
distances[neighbor] = distance + 1;
heap.offer(new Node(distances[neighbor], neighbor));
}
}
}
return distances[k];
}
static class Node {
int distance;
int index;
Node(int distance, int index) {
this.distance = distance;
this.index = index;
}
}
public static void main(String[] args) {
int n;
int k;
Scanner in = new Scanner(System.in);
n = in.nextInt();
k = in.nextInt();
System.out.println("小青到小码家所需的最短时间: " + dijkstra(n, k) + " 分钟");
}
}
已知问题
这段代码会遍历完distances数组(长度为max),不知道是不是该方法带来的局限,感觉效率上不会太高。