diff --git a/MCA算法突击课/第03期/mca_05/Code01_BoatsToSavePeople.java b/MCA算法突击课/第03期/mca_05/Code01_BoatsToSavePeople.java index 4ef0d51..1fe4f5a 100644 --- a/MCA算法突击课/第03期/mca_05/Code01_BoatsToSavePeople.java +++ b/MCA算法突击课/第03期/mca_05/Code01_BoatsToSavePeople.java @@ -7,11 +7,11 @@ import java.util.Arrays; // 每艘船最多坐两人,且不能超过载重 // 想让所有的人同时过河,并且用最好的分配方法让船尽量少 // 返回最少的船数 -// 测试链接 : https://leetcode.com/problems/boats-to-save-people/ +// 测试链接 : https://leetcode.cn/problems/boats-to-save-people/ public class Code01_BoatsToSavePeople { // 首尾双指针的解法 - public static int numRescueBoats2(int[] people, int limit) { + public static int numRescueBoats(int[] people, int limit) { Arrays.sort(people); int ans = 0; int l = 0; diff --git a/MCA算法突击课/第03期/mca_05/Code02_TrappingRainWater.java b/MCA算法突击课/第03期/mca_05/Code02_TrappingRainWater.java index 8f76677..9b704be 100644 --- a/MCA算法突击课/第03期/mca_05/Code02_TrappingRainWater.java +++ b/MCA算法突击课/第03期/mca_05/Code02_TrappingRainWater.java @@ -5,6 +5,30 @@ package 第03期.mca_05; // 本题测试链接 : https://leetcode.cn/problems/trapping-rain-water/ public class Code02_TrappingRainWater { + // 辅助数组的 + public static int zuo(int[] arr) { + int n = arr.length; + if (n < 3) { + return 0; + } + int[] leftMax = new int[n]; + leftMax[0] = arr[0]; + for (int i = 1; i < n; i++) { + leftMax[i] = Math.max(leftMax[i - 1], arr[i]); + } + int[] rightMax = new int[n]; + rightMax[n - 1] = arr[n - 1]; + for (int i = n - 2; i >= 0; i--) { + rightMax[i] = Math.max(rightMax[i + 1], arr[i]); + } + int ans = 0; + for (int i = 1; i < n - 1; i++) { + ans += Math.max(Math.min(leftMax[i - 1], rightMax[i + 1]) - arr[i], 0); + } + return ans; + } + + // 首尾双指针 public static int trap(int[] arr) { if (arr == null || arr.length < 2) { return 0; diff --git a/MCA算法突击课/第03期/mca_05/Code03_LongestSumSubArrayLength.java b/MCA算法突击课/第03期/mca_05/Code03_LongestSumSubArrayLength.java index f181755..4db3f15 100644 --- a/MCA算法突击课/第03期/mca_05/Code03_LongestSumSubArrayLength.java +++ b/MCA算法突击课/第03期/mca_05/Code03_LongestSumSubArrayLength.java @@ -11,20 +11,51 @@ public class Code03_LongestSumSubArrayLength { if (arr == null || arr.length == 0) { return 0; } + // key : 某个前缀和 value : 最早位置 HashMap map = new HashMap(); + // 非常重要 ! map.put(0, -1); - int len = 0; + int ans = 0; int sum = 0; for (int i = 0; i < arr.length; i++) { + // sum : 0.....i 整体的和 sum += arr[i]; if (map.containsKey(sum - k)) { - len = Math.max(i - map.get(sum - k), len); + // 0.......730 1000 k = 100 + // 0...17 900 + // 18..730 + // 长度 = i - 最早位置 + ans = Math.max(i - map.get(sum - k), ans); } if (!map.containsKey(sum)) { map.put(sum, i); } } - return len; + return ans; + } + + public int maxSubArrayLen2(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return 0; + } + // key : 某个前缀和 value : 最早位置 + HashMap map = new HashMap(); + int ans = 0; + int sum = 0; + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; + if (sum == k) { + ans = Math.max(i + 1, ans); + } else { + if (map.containsKey(sum - k)) { + ans = Math.max(i - map.get(sum - k), ans); + } + } + if (!map.containsKey(sum)) { + map.put(sum, i); + } + } + return ans; } } diff --git a/MCA算法突击课/第03期/mca_05/Code08_LRUCache.java b/MCA算法突击课/第03期/mca_05/Code05_LRUCache.java similarity index 80% rename from MCA算法突击课/第03期/mca_05/Code08_LRUCache.java rename to MCA算法突击课/第03期/mca_05/Code05_LRUCache.java index f1512f9..08d8105 100644 --- a/MCA算法突击课/第03期/mca_05/Code08_LRUCache.java +++ b/MCA算法突击课/第03期/mca_05/Code05_LRUCache.java @@ -3,7 +3,7 @@ package 第03期.mca_05; import java.util.HashMap; // 测试链接 : https://leetcode.cn/problems/lru-cache/ -public class Code08_LRUCache { +public class Code05_LRUCache { // 提交以下这个类 public class LRUCache { @@ -43,28 +43,28 @@ public class Code08_LRUCache { } } - public void moveNodeToTail(Node node) { - if (tail == node) { + public void moveNodeToTail(Node x) { + if (tail == x) { return; } - if (head == node) { - head = node.next; + if (head == x) { // x在头部 + head = x.next; head.last = null; } else { - node.last.next = node.next; - node.next.last = node.last; + x.last.next = x.next; + x.next.last = x.last; } - node.last = tail; - node.next = null; - this.tail.next = node; - this.tail = node; + x.last = tail; + x.next = null; + tail.next = x; + tail = x; } public Node removeHead() { if (head == null) { return null; } - Node ans = this.head; + Node ans = head; if (head == tail) { head = null; tail = null; @@ -78,8 +78,11 @@ public class Code08_LRUCache { } + // A -> Node private HashMap keyNodeMap; + // Node 串成的双向链表 private DoubleLinkedList nodeList; + // 这个结构的容量! private final int capacity; public LRUCache(int cap) { @@ -97,12 +100,16 @@ public class Code08_LRUCache { return -1; } + // A 23 public void put(int key, int value) { if (keyNodeMap.containsKey(key)) { Node node = keyNodeMap.get(key); node.val = value; nodeList.moveNodeToTail(node); } else { + // 之前没有A的记录,新增! + // 1) 结构满了 + // 2) 结构没有满 if (keyNodeMap.size() == capacity) { removeMostUnusedCache(); } diff --git a/MCA算法突击课/第03期/mca_05/Code05_UnionFind.java b/MCA算法突击课/第03期/mca_06/Code01_UnionFind.java similarity index 90% rename from MCA算法突击课/第03期/mca_05/Code05_UnionFind.java rename to MCA算法突击课/第03期/mca_06/Code01_UnionFind.java index ab77f12..fd202c2 100644 --- a/MCA算法突击课/第03期/mca_05/Code05_UnionFind.java +++ b/MCA算法突击课/第03期/mca_06/Code01_UnionFind.java @@ -1,7 +1,7 @@ -package 第03期.mca_05; +package 第03期.mca_06; +// 并查集实现 // 测试链接 : https://www.nowcoder.com/questionTerminal/e7ed657974934a30b2010046536a5372 -// 请务必理解这个文件的实现,而且还提供了测试链接 // 提交如下的code,并把主类名改成"Main" // 在测试链接里可以直接通过 // 请同学们务必参考如下代码中关于输入、输出的处理 @@ -13,7 +13,7 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StreamTokenizer; -public class Code05_UnionFind { +public class Code01_UnionFind { public static int MAXN = 1000001; diff --git a/MCA算法突击课/第03期/mca_05/Code06_MostStonesRemovedWithSameRowOrColumn.java b/MCA算法突击课/第03期/mca_06/Code02_MostStonesRemovedWithSameRowOrColumn.java similarity index 95% rename from MCA算法突击课/第03期/mca_05/Code06_MostStonesRemovedWithSameRowOrColumn.java rename to MCA算法突击课/第03期/mca_06/Code02_MostStonesRemovedWithSameRowOrColumn.java index 8c5fa6b..3f8d513 100644 --- a/MCA算法突击课/第03期/mca_05/Code06_MostStonesRemovedWithSameRowOrColumn.java +++ b/MCA算法突击课/第03期/mca_06/Code02_MostStonesRemovedWithSameRowOrColumn.java @@ -1,4 +1,4 @@ -package 第03期.mca_05; +package 第03期.mca_06; import java.util.HashMap; @@ -9,7 +9,7 @@ import java.util.HashMap; // 其中 stones[i] = [xi, yi] 表示第 i 块石头的位置, // 返回 可以移除的石子 的最大数量。 // 测试链接 : https://leetcode.cn/problems/most-stones-removed-with-same-row-or-column/ -public class Code06_MostStonesRemovedWithSameRowOrColumn { +public class Code02_MostStonesRemovedWithSameRowOrColumn { public static int removeStones(int[][] stones) { int n = stones.length; diff --git a/MCA算法突击课/第03期/mca_05/Code07_CouplesHoldingHands.java b/MCA算法突击课/第03期/mca_06/Code03_CouplesHoldingHands.java similarity index 95% rename from MCA算法突击课/第03期/mca_05/Code07_CouplesHoldingHands.java rename to MCA算法突击课/第03期/mca_06/Code03_CouplesHoldingHands.java index 50b4bcb..c413411 100644 --- a/MCA算法突击课/第03期/mca_05/Code07_CouplesHoldingHands.java +++ b/MCA算法突击课/第03期/mca_06/Code03_CouplesHoldingHands.java @@ -1,4 +1,4 @@ -package 第03期.mca_05; +package 第03期.mca_06; // n对情侣坐在连续排列的 2n 个座位上,想要牵到对方的手 // 人和座位由一个整数数组 row 表示,其中 row[i] 是坐在第 i 个座位上的人的ID @@ -6,7 +6,7 @@ package 第03期.mca_05; // 返回 最少交换座位的次数,以便每对情侣可以并肩坐在一起 // 每次交换可选择任意两人,让他们站起来交换座位 // 测试链接 : https://leetcode.cn/problems/couples-holding-hands/ -public class Code07_CouplesHoldingHands { +public class Code03_CouplesHoldingHands { public int minSwapsCouples(int[] row) { int n = row.length; diff --git a/MCA算法突击课/第03期/mca_06/Code04_TopologicalSort.java b/MCA算法突击课/第03期/mca_06/Code04_TopologicalSort.java new file mode 100644 index 0000000..ac7d08d --- /dev/null +++ b/MCA算法突击课/第03期/mca_06/Code04_TopologicalSort.java @@ -0,0 +1,89 @@ +package 第03期.mca_06; + +// 根据入度来求拓扑排序,牛客网的测试数据 +// 测试链接 : https://www.nowcoder.com/questionTerminal/88f7e156ca7d43a1a535f619cd3f495c +// 请同学们务必参考如下代码中关于输入、输出的处理 +// 这是输入输出处理效率很高的写法 +// 提交以下所有代码,把主类名改成Main,可以直接通过 +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.ArrayList; +import java.util.Arrays; + +public class Code04_TopologicalSort { + + public static int MAXN = 200001; + + public static int[] queue = new int[MAXN]; + + public static int[] indegree = new int[MAXN]; + + public static int[] ans = new int[MAXN]; + + public static int n, m, from, to; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + while (in.nextToken() != StreamTokenizer.TT_EOF) { + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + ArrayList> graph = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + graph.add(new ArrayList<>()); + } + for (int i = 0; i < m; i++) { + in.nextToken(); + from = (int) in.nval; + in.nextToken(); + to = (int) in.nval; + graph.get(from).add(to); + } + if (!topoSort(graph)) { + out.println(-1); + } else { + for (int i = 0; i < n - 1; i++) { + out.print(ans[i] + " "); + } + out.println(ans[n - 1]); + } + out.flush(); + } + } + + // 有拓扑排序返回true + // 没有拓扑排序返回false + public static boolean topoSort(ArrayList> graph) { + Arrays.fill(indegree, 1, n + 1, 0); + for (ArrayList nexts : graph) { + for (int next : nexts) { + indegree[next]++; + } + } + int l = 0; + int r = 0; + for (int i = 1; i <= n; i++) { + if (indegree[i] == 0) { + queue[r++] = i; + } + } + int cnt = 0; + while (l < r) { + int cur = queue[l++]; + ans[cnt++] = cur; + for (int next : graph.get(cur)) { + if (--indegree[next] == 0) { + queue[r++] = next; + } + } + } + return cnt == n; + } + +} diff --git a/MCA算法突击课/第03期/mca_06/Code05_Kruskal.java b/MCA算法突击课/第03期/mca_06/Code05_Kruskal.java new file mode 100644 index 0000000..170f549 --- /dev/null +++ b/MCA算法突击课/第03期/mca_06/Code05_Kruskal.java @@ -0,0 +1,99 @@ +package 第03期.mca_06; + +// 课上没讲这个实现 +// 因为是一样的,都是用Kruskal算法实现最小生成树,只不过是牛客网的测试数据 +// 测试链接 : https://www.nowcoder.com/questionTerminal/c23eab7bb39748b6b224a8a3afbe396b +// 请同学们务必参考如下代码中关于输入、输出的处理 +// 这是输入输出处理效率很高的写法 +// 提交以下所有代码,把主类名改成Main,可以直接通过 +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code05_Kruskal { + + public static int MAXM = 100001; + + public static int[][] edges = new int[MAXM][3]; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + while (in.nextToken() != StreamTokenizer.TT_EOF) { + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + for (int i = 0; i < m; i++) { + in.nextToken(); + edges[i][0] = (int) in.nval; + in.nextToken(); + edges[i][1] = (int) in.nval; + in.nextToken(); + edges[i][2] = (int) in.nval; + } + Arrays.sort(edges, 0, m, (a, b) -> a[2] - b[2]); + build(n); + int ans = 0; + for (int[] edge : edges) { + if (union(edge[0], edge[1])) { + ans += edge[2]; + } + } + out.println(ans); + out.flush(); + } + } + + // 下面是并查集结构 + public static int MAXN = 10001; + + public static int[] father = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static int[] help = new int[MAXN]; + + public static void build(int n) { + for (int i = 1; i <= n; i++) { + father[i] = i; + size[i] = 1; + } + } + + private static int find(int i) { + int size = 0; + while (i != father[i]) { + help[size++] = i; + i = father[i]; + } + while (size > 0) { + father[help[--size]] = i; + } + return i; + } + + // 如果i和j,原本是一个集合,返回false + // 如果i和j,不是一个集合,合并,然后返回true + public static boolean union(int i, int j) { + int fi = find(i); + int fj = find(j); + if (fi != fj) { + if (size[fi] >= size[fj]) { + father[fj] = fi; + size[fi] += size[fj]; + } else { + father[fi] = fj; + size[fj] += size[fi]; + } + return true; + } else { + return false; + } + } + +} diff --git a/MCA算法突击课/第03期/mca_06/Code06_OptimizeWaterDistributionInVillage.java b/MCA算法突击课/第03期/mca_06/Code06_OptimizeWaterDistributionInVillage.java new file mode 100644 index 0000000..ee60e52 --- /dev/null +++ b/MCA算法突击课/第03期/mca_06/Code06_OptimizeWaterDistributionInVillage.java @@ -0,0 +1,90 @@ +package 第03期.mca_06; + +import java.util.Arrays; + +// 村里面一共有 n 栋房子 +// 我们希望通过建造水井和铺设管道来为所有房子供水。 +// 对于每个房子 i,我们有两种可选的供水方案: +// 一种是直接在房子内建造水井, +// 成本为 wells[i - 1](注意 -1 ,因为 索引从0开始) +// 另一种是从另一口井铺设管道引水, +// 数组 pipes 给出了在房子间铺设管道的成本 +// 其中每个 pipes[j] = [house1j, house2j, costj] +// 代表用管道将 house1j 和 house2j连接在一起的成本 +// 连接是双向的 +// 请返回 为所有房子都供水的最低总成本 +// 测试链接 : https://leetcode.cn/problems/optimize-water-distribution-in-a-village/ +public class Code06_OptimizeWaterDistributionInVillage { + + public static int MAXN = 10010; + + public static int[][] edges = new int[MAXN << 1][3]; + + public static int esize; + + public static int[] father = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static int[] help = new int[MAXN]; + + public static void build(int n) { + for (int i = 0; i <= n; i++) { + father[i] = i; + size[i] = 1; + } + } + + public static int find(int i) { + int s = 0; + while (i != father[i]) { + help[s++] = i; + i = father[i]; + } + while (s > 0) { + father[help[--s]] = i; + } + return i; + } + + public static boolean union(int i, int j) { + int f1 = find(i); + int f2 = find(j); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + father[f2] = f1; + size[f1] += size[f2]; + } else { + father[f1] = f2; + size[f2] += size[f1]; + } + return true; + } else { + return false; + } + } + + public static int minCostToSupplyWater(int n, int[] wells, int[][] pipes) { + esize = 0; + for (int i = 0; i < n; i++, esize++) { + edges[esize][0] = 0; + edges[esize][1] = i + 1; + edges[esize][2] = wells[i]; + } + for (int i = 0; i < pipes.length; i++, esize++) { + edges[esize][0] = pipes[i][0]; + edges[esize][1] = pipes[i][1]; + edges[esize][2] = pipes[i][2]; + } + Arrays.sort(edges, 0, esize, (a, b) -> a[2] - b[2]); + build(n); + int ans = 0; + for (int i = 0; i < esize; i++) { + if (union(edges[i][0], edges[i][1])) { + ans += edges[i][2]; + } + } + return ans; + } + +} diff --git a/MCA算法突击课/第03期/mca_06/Code07_NetworkDelayTime.java b/MCA算法突击课/第03期/mca_06/Code07_NetworkDelayTime.java new file mode 100644 index 0000000..04c63b0 --- /dev/null +++ b/MCA算法突击课/第03期/mca_06/Code07_NetworkDelayTime.java @@ -0,0 +1,41 @@ +package 第03期.mca_06; + +import java.util.ArrayList; +import java.util.PriorityQueue; + +// Dijkstra算法 +// leetcode 743题,可以用这道题来练习Dijkstra算法 +// 测试链接 : https://leetcode.com/problems/network-delay-time +public class Code07_NetworkDelayTime { + + public static int networkDelayTime(int[][] times, int n, int k) { + ArrayList> 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 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; + } + +} diff --git a/MCA算法突击课/第03期/ppt/key/第06节.key b/MCA算法突击课/第03期/ppt/key/第06节.key new file mode 100644 index 0000000..b26643e Binary files /dev/null and b/MCA算法突击课/第03期/ppt/key/第06节.key differ diff --git a/MCA算法突击课/第03期/ppt/powerpoint/第06节.pptx b/MCA算法突击课/第03期/ppt/powerpoint/第06节.pptx new file mode 100644 index 0000000..dfe9bd6 Binary files /dev/null and b/MCA算法突击课/第03期/ppt/powerpoint/第06节.pptx differ