Graph Summary
BFS
No. | title | difficulty |
---|---|---|
207 | Course Schedule I/II | Medium |
DFS
Union Find
Shortest path(unweighted)
- Minimum Genetic Mutation
Weighted shortest path
单源最短路经算法
1. Dijkstra’s Algorithm
这个算法其实是贪心,对一个节点, 每次选择最近能到达的点,并且更新临接顶点的最近距离。 因为每次选择最近的点, 所以 Dijkstra 不能够处理负边的情况。
Dijkstra 实现
- 邻接矩阵 对每一个点都要扫一遍邻接点,找到最小的。 所以,复杂度 $O(V^2)$
- Heap 既然要找到最小的顶点,用heap存信息便可。 这样的复杂度就是 $O(ElogV)$
- Java 中 heap 不支update操作 即一个点发现更短的距离需要更新时,无法把当前节点update priority 或者删除重新插入。折中的方法就是保存老的节点不动,在插入一个相同的路径更短的相同节点。 这样使得 算法复杂度到达了 $O(ElogE)$.
743. Network Delay Time
class Solution { public int networkDelayTime(int[][] times, int N, int K) { if(times == null || times.length == 0) return 0; int[][] graph = new int[N + 1][N + 1]; for(int[] row : graph) Arrays.fill(row, -1); for(int i = 0; i < times.length; i++) { graph[times[i][0]][times[i][1]]= times[i][2]; } PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) ->(a[1] - b[1])); pq.offer(new int[]{K, 0}); Set<Integer> visited = new HashSet<>(); while(!pq.isEmpty()) { int[] cur = pq.poll(); //if(visited.contains(cur[0])) continue; visited.add(cur[0]); if(visited.size() == N) return cur[1]; for(int i = 1; i < graph[0].length; i++) { if(graph[cur[0]][i] == -1 || visited.contains(i)) continue; int dist = graph[cur[0]][i] + cur[1]; pq.offer(new int[]{i, dist}); } } return -1; } }
Minimum Spinning Tree
Kruskal 算法
Kruskal 算法描述。按照边的长短从小到大排序。 每次选取一个边,尝试加入图中,如果造成环路的话,舍弃改边。 环路的判断可以用union find 来做。
1584. Min Cost to Connect All Points
class Solution {
public boolean union(int[] tf, int x, int y) {
int a = find(tf, x);
int b = find(tf, y);
if(a == b) return false;
else {
tf[b] = a;
return true;
}
}
public int find(int[] tf, int x) {
if(tf[x] == x) return x;
tf[x] = find(tf, tf[x]);
return tf[x];
}
public int minCostConnectPoints(int[][] points) {
int n = points.length;
int[][] dist = new int[n][n];
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> dist[a[0]][a[1]] - dist[b[0]][b[1]]);
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
dist[i][j] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
pq.offer(new int[]{i, j});
}
}
int cnt = n;
int[] tf = new int[n];
for(int i = 0; i < tf.length; i++) tf[i] = i;
int ret = 0;
while(!pq.isEmpty() && cnt > 1) {
int[] point = pq.poll();
if(union(tf, point[0], point[1])) {
ret += dist[point[0]][point[1]];
cnt--;
}
}
return ret;
}
}
Prim 算法
Prim 算法和 Dijstra 神似。 都是贪心。 但是贪心贪法不同。 Dijkstra 是贪从源点到当前最近的节点。 而prim不需要记录最近的路径。 它只需要每次找出当前点的最近临接点即可。 所以每次往 pq 里面push 是当前点与剩下所有点之间的距离。
class Solution {
public int minCostConnectPoints(int[][] points) {
int n = points.length;
int[][] dist = new int[n][n];
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (a[1] - b[1]));
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
dist[i][j] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
dist[j][i] = dist[i][j];
}
}
HashSet<Integer> used = new HashSet<>();
pq.offer(new int[]{0, 0});
int ret = 0;
while(!pq.isEmpty()) {
int[] cur = pq.poll();
if(used.contains(cur[0])) continue;
ret += cur[1];
used.add(cur[0]);
if(used.size() == n) return ret;
for(int i = 0; i < dist[0].length; i++) {
if(used.contains(i)) continue;
int path = dist[cur[0]][i];
pq.offer(new int[]{i, path});
}
}
return -1;
}
}