1.思路分析:
一道Dijkstra模板题
推荐Dijkstra算法讲解教程
Dijkstra(有向图某点到其他所有点的最短路径问题)
Dijkstra算法的基本思想是贪心策略,每次从未确定最短路径的顶点中选择距离源点最近的一个,然后以该顶点为中介,更新其他顶点到源点的距离。这个过程重复执行,直到所有顶点都确定了最短路径。
Dijkstra算法的时间复杂度取决于使用的数据结构,如果使用数组或链表存储顶点集合,时间复杂度为O(n^2),如果使用优先队列或二叉堆存储顶点集合,时间复杂度为O((m+n)logn),其中n是顶点数,m是边数。
Dijkstra算法的一个重要条件是图中不能有负权边,否则算法可能无法得到正确的结果
本题思路
创建一个具有双向链表的节点类Node,
class Node{
int key;
int value;
Node prev;
Node next;
public Node(int value){
this.value = value;
}
}
创建一个双向链表类DoubleLinkList,实现头尾虚拟节点,并且初始化他们的相互连接。
并且实现删除头结点,插入节点,插入尾节点功能。
再借助DoubleLinkList实现题目中的LRUCache类。
2. 算法实现
static int[][] map ;
static int INF = Integer.MAX_VALUE;
static int[] dis;
static int[] flag;
public static int networkDelayTime(int[][] times, int n, int k) {
//n是点的数量,k是起点
map = new int[n][n];//点与点之间的边连接情况
dis = new int[n];//最短路径数组
flag = new int[n];//已访问过的数组
flag[k - 1] = 1;
for(int i = 0;i < n;i++){//初始化图,各个点之间都不连通
for(int j = 0;j < n;j++){
map[i][j] = INF;
}
}
for(int i = 0;i < times.length;i++){//将题目中的边初始化到图中
map[times[i][0] - 1][times[i][1] - 1] = times[i][2];
}
for(int i = 0;i < n;i++){//将起始点到其他各个点的距离初始化到最短路径数组中
dis[i] = map[k - 1][i];
}
dis[k - 1] = 0;//自己到自己初始化为0
//Dijikstra
int loop = 0;
while(loop < n - 1){//循环总点数-1次,访问其他点到源点的最短距离
int min = INF;
int minIndex = -1;
for(int i = 0;i < n;i++){//找到当前还未访问的点中距离源地最近的点
if(min > dis[i] && flag[i] == 0){
min = dis[i];
minIndex = i;
}
}
if(minIndex != -1){//当前距离源点最近的点标记为访问过
flag[minIndex] = 1;
}else{//当前未访问的点到源点都不可达
return -1;
}
for(int i = 0;i < n;i++){//以当前到源点最近的点为中间点
if(map[minIndex][i] != INF && flag[i] != 1){//遍历的点未被访问过,并且当前中间点到该点可达
if(dis[minIndex] + map[minIndex][i] < dis[i]){
dis[i] = dis[minIndex] + map[minIndex][i];
}
}
}
loop++;
}
int max = Integer.MIN_VALUE;
for(int val : dis){
if(val == INF){
return -1;
}else if(max < val){
max = val;
}
}
return max;
}
3. 算法坑点
- 注意笨题解采用邻接表实现图,题目给出的是从1开始的序号,记得把序号-1,再对map中对应下标的值进行操作。(因为map是二维数组,下标默认从0开始)
- 以下这段代码是找当前还未访问过的点钟距离源点最近的点,
int min = INF;
int minIndex = -1;
for(int i = 0;i < n;i++){//找到当前还未访问的点中距离源地最近的点
if(min > dis[i] && flag[i] == 0){
min = dis[i];
minIndex = i;
}
}
flag[minIndex] = 1;//当前距离源点最近的点标记为访问过
但是min初始化为INF,有可能有个潜在的情况,当前未访问的所有的点到源点都是不可达,即距离为INF,此时在flag[minIndex] = 1赋值前做一个判断。
if(minIndex != -1){//当前距离源点最近的点标记为访问过
flag[minIndex] = 1;
}else{//当前未访问的点到源点都不可达
return -1;
}
- 在比较源点到中间点最短距离+中间点到剩余未访问的点距离与源点到中间点最短距离时,注意其他点应该是未被访问过的。
if(flag[i] != 1 && map[minIndex][i] != INF)//flag[i] != 1 未被访问过的,并且中间点到未访问点之间可达
为什么其他点应该是未被访问过的,因为访问过的点已经找到最短路径了。