modify code

master
algorithmzuo 3 years ago
parent fddc2916aa
commit ac9c3d6b0f

@ -0,0 +1,151 @@
package class16;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
// 课上没有讲这个题,这是我给同学们找的练习题
// leetcode 743题可以用这道题来练习Dijkstra算法
// 测试链接 : https://leetcode.com/problems/network-delay-time
public class Code06_NetworkDelayTime {
// 方法一 : 普通堆 + 屏蔽已经计算过的点
public static int networkDelayTime1(int[][] times, int n, int k) {
ArrayList<ArrayList<int[]>> nexts = new ArrayList<>();
for (int i = 0; i <= n; i++) {
nexts.add(new ArrayList<>());
}
for (int[] delay : times) {
nexts.get(delay[0]).add(new int[] { delay[1], delay[2] });
}
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[1] - b[1]);
heap.add(new int[] { k, 0 });
boolean[] used = new boolean[n + 1];
int num = 0;
int max = 0;
while (!heap.isEmpty() && num < n) {
int[] record = heap.poll();
int cur = record[0];
int delay = record[1];
if (used[cur]) {
continue;
}
used[cur] = true;
num++;
max = Math.max(max, delay);
for (int[] next : nexts.get(cur)) {
heap.add(new int[] { next[0], delay + next[1] });
}
}
return num < n ? -1 : max;
}
// 方法二 : 加强堆的解法
public static int networkDelayTime2(int[][] times, int n, int k) {
ArrayList<ArrayList<int[]>> nexts = new ArrayList<>();
for (int i = 0; i <= n; i++) {
nexts.add(new ArrayList<>());
}
for (int[] delay : times) {
nexts.get(delay[0]).add(new int[] { delay[1], delay[2] });
}
Heap heap = new Heap(n);
heap.add(k, 0);
int num = 0;
int max = 0;
while (!heap.isEmpty()) {
int[] record = heap.poll();
int cur = record[0];
int delay = record[1];
num++;
max = Math.max(max, delay);
for (int[] next : nexts.get(cur)) {
heap.add(next[0], delay + next[1]);
}
}
return num < n ? -1 : max;
}
// 加强堆
public static class Heap {
public boolean[] used;
public int[][] heap;
public int[] hIndex;
public int size;
public Heap(int n) {
used = new boolean[n + 1];
heap = new int[n + 1][2];
hIndex = new int[n + 1];
Arrays.fill(hIndex, -1);
size = 0;
}
public void add(int cur, int delay) {
if (used[cur]) {
return;
}
if (hIndex[cur] == -1) {
heap[size][0] = cur;
heap[size][1] = delay;
hIndex[cur] = size;
heapInsert(size++);
} else {
int hi = hIndex[cur];
if (delay <= heap[hi][1]) {
heap[hi][1] = delay;
heapInsert(hi);
}
}
}
public int[] poll() {
int[] ans = heap[0];
swap(0, --size);
heapify(0);
used[ans[0]] = true;
hIndex[ans[0]] = -1;
return ans;
}
public boolean isEmpty() {
return size == 0;
}
private void heapInsert(int i) {
int parent = (i - 1) / 2;
while (heap[i][1] < heap[parent][1]) {
swap(i, parent);
i = parent;
parent = (i - 1) / 2;
}
}
private void heapify(int i) {
int l = (i * 2) + 1;
while (l < size) {
int smallest = l + 1 < size && heap[l + 1][1] < heap[l][1] ? (l + 1) : l;
smallest = heap[smallest][1] < heap[i][1] ? smallest : i;
if (smallest == i) {
break;
}
swap(smallest, i);
i = smallest;
l = (i * 2) + 1;
}
}
private void swap(int i, int j) {
int[] o1 = heap[i];
int[] o2 = heap[j];
int o1hi = hIndex[o1[0]];
int o2hi = hIndex[o2[0]];
heap[i] = o2;
heap[j] = o1;
hIndex[o1[0]] = o2hi;
hIndex[o2[0]] = o1hi;
}
}
}

@ -1,192 +0,0 @@
package class16;
import java.util.PriorityQueue;
// 本文件没有在课上讲
// 因为是之后学员单独问的问题所以我用邻接矩阵的方式又实现了一遍Dijkstra
// 题目描述如下
//
// 一天Peiger捧着一本世界地图在看突然他拿起笔
// 将他最爱的那些城市标记出来,并且随机的将这些城市中的某些用线段两两连接起来。
// Peiger量出了每条线段的长度现在Peiger想知道在这些线段组成的图中任意两个城市之间的最短距离是多少。
//
// 输入
// 输入包含多组测试数据。
// 每组输入第一行为两个正整数nn<=10和mm<=n*(n-1)/2n表示城市个数m表示线段个数。
// 接下来m行每行输入三个整数ab和l表示a市与b市之间存在一条线段线段长度为l。a与b不同
// 每组最后一行输入两个整数x和y表示问题x市与y市之间的最短距离是多少。x与y不同
// 城市标号为1~nl<=20。
//
// 输出
// 对于每组输入输出x市与y市之间的最短距离如果x市与y市之间非连通则输出“No path”。
//
// 样例输入
// 4 4
// 1 2 4
// 1 3 1
// 1 4 1
// 2 3 1
// 2 4
//
// 样例输出
// 3
public class Code07_XtoYMinDistance {
// 暴力方法
// dfs尝试所有情况
// 没有优化,就是纯暴力
public static int minDistance1(int n, int m, int[][] roads, int x, int y) {
// 第一步生成邻接矩阵
int[][] map = new int[n + 1][n + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
map[i][j] = Integer.MAX_VALUE;
}
}
for (int[] road : roads) {
map[road[0]][road[1]] = Math.min(map[road[0]][road[1]], road[2]);
map[road[1]][road[0]] = Math.min(map[road[1]][road[0]], road[2]);
}
boolean[] visited = new boolean[n + 1];
return process(x, y, n, map, visited);
}
// 当前来到的城市是cur最终目的地是aim一共有1~n这些城市
// 所有城市之间的距离都在map里
// 之前已经走过了哪些城市都记录在了visited里面请不要重复经过
// 返回从cur到aim所有可能的路里最小距离是多少
public static int process(int cur, int aim, int n, int[][] map, boolean[] visited) {
if (visited[cur]) {
return Integer.MAX_VALUE;
}
if (cur == aim) {
return 0;
}
visited[cur] = true;
int ans = Integer.MAX_VALUE;
for (int next = 1; next <= n; next++) {
if (next != cur && map[cur][next] != Integer.MAX_VALUE) {
int rest = process(next, aim, n, map, visited);
if (rest != Integer.MAX_VALUE) {
ans = Math.min(ans, map[cur][next] + rest);
}
}
}
visited[cur] = false;
return ans;
}
// Dijkstra的解
public static int minDistance2(int n, int m, int[][] roads, int x, int y) {
// 第一步生成邻接矩阵
int[][] map = new int[n + 1][n + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
map[i][j] = Integer.MAX_VALUE;
}
}
for (int[] road : roads) {
map[road[0]][road[1]] = Math.min(map[road[0]][road[1]], road[2]);
map[road[1]][road[0]] = Math.min(map[road[1]][road[0]], road[2]);
}
// computed[i] = true表示从源出发点到i这个城市已经计算出最短距离了
// computed[i] = false表示从源出发点到i这个城市还没有计算出最短距离
boolean[] computed = new boolean[n + 1];
// 距离小根堆
PriorityQueue<Node> heap = new PriorityQueue<>((a, b) -> (a.pathSum - b.pathSum));
heap.add(new Node(x, 0));
while (!heap.isEmpty()) {
Node cur = heap.poll();
if (computed[cur.city]) {
continue;
}
if (cur.city == y) {
return cur.pathSum;
}
computed[cur.city] = true;
for (int next = 1; next <= n; next++) {
if (next != cur.city && map[cur.city][next] != Integer.MAX_VALUE && !computed[next]) {
heap.add(new Node(next, cur.pathSum + map[cur.city][next]));
}
}
}
return Integer.MAX_VALUE;
}
// 当前来到的Node注意这不是城市的意思这是就是一个普通的封装类
// Node封装了当前来到的城市是什么以及从源出发点到这个城市的路径和是多少
public static class Node {
// 当前来到的城市编号
public int city;
// 从源出发点到这个城市的路径和
public int pathSum;
public Node(int c, int p) {
city = c;
pathSum = p;
}
}
// 为了测试
// 城市1~n
// 随机生成m条道路
// 每一条路的距离在1~v之间
public static int[][] randomRoads(int n, int m, int v) {
int[][] roads = new int[m][3];
for (int i = 0; i < m; i++) {
int from = (int) (Math.random() * n) + 1;
int to = (int) (Math.random() * n) + 1;
int distance = (int) (Math.random() * v) + 1;
roads[i] = new int[] { from, to, distance };
}
return roads;
}
// 为了测试
public static void main(String[] args) {
// 城市数量n下标从1开始不从0开始
int n = 4;
// 边的数量mm的值不能大于n * (n-1) / 2
int m = 4;
// 所的路有m条
// [a,b,c]表示a和b之间有路距离为3根据题意本题中的边都是无向边
// 假设有两条路
// [1,3,7]这条路是从1到3距离是7
// [1,3,4]这条路是从1到3距离是4
// 那么应该忽略[1,3,7],因为[1,3,4]比它好
int[][] roads = new int[m][3];
roads[0] = new int[] { 1, 2, 4 };
roads[1] = new int[] { 1, 3, 1 };
roads[2] = new int[] { 1, 4, 1 };
roads[3] = new int[] { 2, 3, 1 };
// 求从x到y的最短距离是多少x和y应该在[1,n]之间
int x = 2;
int y = 4;
// 暴力方法的解
System.out.println(minDistance1(n, m, roads, x, y));
// Dijkstra的解
System.out.println(minDistance2(n, m, roads, x, y));
// 下面开始随机验证
int cityMaxSize = 12;
int pathMax = 30;
int testTimes = 20000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
n = (int) (Math.random() * cityMaxSize) + 1;
m = (int) (Math.random() * n * (n - 1) / 2) + 1;
roads = randomRoads(n, m, pathMax);
x = (int) (Math.random() * n) + 1;
y = (int) (Math.random() * n) + 1;
int ans1 = minDistance1(n, m, roads, x, y);
int ans2 = minDistance2(n, m, roads, x, y);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}
Loading…
Cancel
Save