Dijkstra、Floyed 总结
大致顺序
- 构建图、赋值
- 根据 Floyed 或者 Dijkstra 算法或者节点之间的最小距离( 一般为 dp[][] 或者 dist[] )
- 最后根据题干获取答案
构建图、赋值
一般为:
public void init(int[][] grid, int k, int n) {
// 1. 构造图
int[][] dp = new int[n][n];
int INF = Integer.MAX_VALUE / 2;
// 任意节点之间的距离为 无限大
// 节点本身之间的距离为 0
for(int i = 0; i < n; i++) {
Arrays.fill(dp, INF);
dp[i][i] = 0;
}
// 2. 赋值
for(int i = 0; i < grid.length; i++) {
int x = grid[i][0] - 1;
int y = grid[i][1] - 1;
dp[x][y] = grid[i][2];
}
根据 Floyed 或者 Dijkstra 算法的核心部分
- Floyed
// 依次遍历每个节点
for(int k = 0; k < n; k++) {
// 枚举所有距离
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
// 当前距离 或者 通过其他节点 最小
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k][j]);
}
}
}
- Dijkstra
- 从未加入的节点中选取一个 dist 最小的节点,收录进来
- 更新收录节点周边节点的 dist
boolean[] visited = new boolean[n]; // 是否遍历过
int[] dist = new int[n]; // 存储源节点 k 到其他节点的最小距离
Arrays.fill(dist, INF);
dist[k - 1] = 0; // 节点本身的距离为 0
for(int i = 0; i < n; i++) {
int cur = -1;
for(int j = 0; j < n; j++) {
// 从未加入的节点中选取一个dist最小的节点,收录进来
if(!visited[j] && ( cur == -1 || dist[cur] > dist[j]) {
cur = j;
}
}
visited[cur] = true;
// 这里其实我感觉跟 Floyed 没什么区别, 都是一个意思
// dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k][j]);
for(int j = 0; j < n; j++) {
dist[j] = Math.min(dist[j], dist[cur] + dp[cur][j];
}
}
Floyed、Dijkstra、Bellman-Ford
Dijkstra 迪杰斯特拉
- Dijkstra 算法是通过选定的被访问节点,求一个顶点到其他顶点的最短路径,也就是单源最短路径
- Dijkstra 算法只能解决边权重非负的最短路径问题
- 属于贪心算法,时间复杂度为 O(n^2)
Floyed 弗洛伊德
- Floyed 算法是任意两点之间的最短路径,是多源最短路径;每个节点都可以看作被访问节点,求每个节点到其他节点的最短路径
- 属于动态规划算法,时间复杂度为 O(n^3)
Bellman-Ford 福特
- Bellman-Ford 算法是解决单源最短路劲问题,支持边权重为负数的情况,多数适用于有额外的条件、例如:在k个中转之内、规定时间内到达等情况
- 属于动态规划算法,时间复杂度为 O(VE) V: 顶点数量、E:边数
- 我感觉几乎就是固定格式了
有向图
- K 站中转内最便宜的航班
class Solution {
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
int INF = Integer.MAX_VALUE / 2;
int[][] dp = new int[k + 2][n];
// 初始化
for(int i = 0; i < dp.length; i++) {
Arrays.fill(dp[i], INF);
}
// 初始化 0 到 src 的初始距离
dp[0][src] = 0;
// 枚举 n - 1条边
for(int i = 1; i <= k + 1; i++) {
for(int[] temp : flights) {
int start = temp[0], end = temp[1], cost = temp[2];
dp[i][end] = Math.min(dp[i][end], dp[i - 1][start] + cost);
}
}
int res = INF;
for(int i = 1; i <= k + 1; i++) {
res = Math.min(res, dp[i][dst]);
}
return res == INF ? -1 : res;
}
}
无向图
- 多一步 dp
- 规定时间内到达终点的最小花费
class Solution {
public int minCost(int maxTime, int[][] edges, int[] passingFees) {
int n = passingFees.length;
int INF = Integer.MAX_VALUE / 2;
int[][] dp = new int[maxTime + 1][n];
for(int i = 0; i < dp.length; i++) {
Arrays.fill(dp[i], INF);
}
dp[0][0] = passingFees[0];
for(int i = 1; i <= maxTime; i++) {
for(int[] temp : edges) {
int start = temp[0], end = temp[1], cost = temp[2];
if(i >= cost) {
dp[i][end] = Math.min(dp[i][end], dp[i - cost][start] + passingFees[end]);
dp[i][start] = Math.min(dp[i][start], dp[i - cost][end] + passingFees[start]);
}
}
}
int res = INF;
for(int i = 1; i <= maxTime; i++) {
res = Math.min(res, dp[i][n - 1]);
}
return res == INF ? -1 : res;
}
}