From 0e843ed49b819898985e53ea03949d9e15e87998 Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Mon, 17 Oct 2022 16:35:03 +0800 Subject: [PATCH] modify code --- .../Code01_RangesHasDominateNumber.java | 107 +++++++++ .../Code02_BestMedianPickAdjacent.java | 215 ++++++++++++++++++ .../Code03_FindMazeMinPath.java | 128 +++++++++++ .../Code04_ShortestPathToGetAllKeys.java | 131 +++++++++++ .../Code05_SoldierFindEnemy.java | 202 ++++++++++++++++ ...养的大厂算法面试题(正在直播) | 77 +++++++ 6 files changed, 860 insertions(+) create mode 100644 算法周更班/class_2022_10_2_week/Code01_RangesHasDominateNumber.java create mode 100644 算法周更班/class_2022_10_2_week/Code02_BestMedianPickAdjacent.java create mode 100644 算法周更班/class_2022_10_2_week/Code03_FindMazeMinPath.java create mode 100644 算法周更班/class_2022_10_2_week/Code04_ShortestPathToGetAllKeys.java create mode 100644 算法周更班/class_2022_10_2_week/Code05_SoldierFindEnemy.java diff --git a/算法周更班/class_2022_10_2_week/Code01_RangesHasDominateNumber.java b/算法周更班/class_2022_10_2_week/Code01_RangesHasDominateNumber.java new file mode 100644 index 0000000..d55f505 --- /dev/null +++ b/算法周更班/class_2022_10_2_week/Code01_RangesHasDominateNumber.java @@ -0,0 +1,107 @@ +package class_2022_10_2_week; + +import java.util.HashMap; + +// 来自小红书 +// 小A认为如果在数组中有一个数出现了至少k次 +// 且这个数是该数组的众数,即出现次数最多的数之一 +// 那么这个数组被该数所支配 +// 显然当k比较大的时候,有些数组不被任何数所支配 +// 现在小A拥有一个长度为n的数组,她想知道内部有多少个区间是被某个数支配的 +// 2 <= k <= n <= 100000 +// 1 <= 数组的值 <= n +public class Code01_RangesHasDominateNumber { + + // 暴力方法 + // 为了验证 + // 时间复杂度O(N^3) + public static int dominates1(int[] arr, int k) { + int n = arr.length; + int ans = 0; + for (int l = 0; l < n; l++) { + for (int r = l; r < n; r++) { + if (ok(arr, l, r, k)) { + ans++; + } + } + } + return ans; + } + + public static boolean ok(int[] arr, int l, int r, int k) { + HashMap map = new HashMap(); + for (int i = l; i <= r; i++) { + map.put(arr[i], map.getOrDefault(arr[i], 0) + 1); + } + for (int times : map.values()) { + if (times >= k) { + return true; + } + } + return false; + } + + // 正式方法 + // 时间复杂度O(N) + public static int dominates2(int[] arr, int k) { + int n = arr.length; + // 总数量 + int all = n * (n + 1) / 2; + // 不被支配的区间数量 + int except = 0; + // 次数表 + // 0 : 0 + // 1 : 0 + // 2 : 0 + int[] cnt = new int[n + 1]; + // l ... r + // 窗口用这个形式[l,r) + // l...r-1 r(x) + // l == 0 r == 0 [l,r) 一个数也没有 + // l == 0 r == 1 [0..0] + for (int l = 0, r = 0; l < n; l++) { + // [r] 即将要进来的 + // cnt[arr[r]] + 1 < k + while (r < n && cnt[arr[r]] + 1 < k) { + // cnt[arr[r]]++; + // r++ + cnt[arr[r++]]++; + } + // l..l + // l..l+1 + // l..l+2 + // l..r-1 + except += r - l; + cnt[arr[l]]--; + } + return all - except; + } + + // 为了测试 + public static int[] randomArray(int n) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * n) + 1; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 100; + int testTimes = 5000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int[] arr = randomArray(n); + int k = (int) (Math.random() * n) + 1; + int ans1 = dominates1(arr, k); + int ans2 = dominates2(arr, k); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_10_2_week/Code02_BestMedianPickAdjacent.java b/算法周更班/class_2022_10_2_week/Code02_BestMedianPickAdjacent.java new file mode 100644 index 0000000..e069fab --- /dev/null +++ b/算法周更班/class_2022_10_2_week/Code02_BestMedianPickAdjacent.java @@ -0,0 +1,215 @@ +package class_2022_10_2_week; + +import java.util.Arrays; + +// 来自京东 +// 实习岗位笔试题 +// 给定一个数组arr,长度为n +// 任意相邻的两个数里面至少要有一个被选出来,组成子序列,才是合法的! +// 求所有可能的合法子序列中,最大中位数是多少 +// 中位数的定义为上中位数 +// [1, 2, 3, 4]的上中位数是2 +// [1, 2, 3, 4, 5]的上中位数是3 +// 2 <= n <= 10^5 +// 1 <= arr[i] <= 10^9 +// 我写的帖子解答 : https://www.mashibing.com/question/detail/34771 +public class Code02_BestMedianPickAdjacent { + + public static int validSubMaxSum(int[] arr) { + return zuo(arr, 0, 1); + } + + // 当前来到i位置 + // 如果arr[i-1]位置的数选了,pre == 1 + // 如果arr[i-1]位置的数没选,pre == 0 + // arr[i....]最大合法子序列的累加和是多少 + public static int zuo(int[] arr, int i, int pre) { + if (i == arr.length) { + return 0; + } + // 还有数! + // 可能性1 : 不要i位置的数 + int p1 = Integer.MIN_VALUE; + if (pre == 1) { + p1 = zuo(arr, i + 1, 0); + } + // 可能性2 : 要i位置的数 + int p2 = Integer.MIN_VALUE; + int next2 = zuo(arr, i + 1, 1); + if (next2 != Integer.MIN_VALUE) { + p2 = arr[i] + next2; + } + return Math.max(p1, p2); + } + + // 启发函数 + // 如果数组中的值只有1和-1, + // 你可以从左往右选择数字组成子序列, + // 但是要求任何两个相邻的数,至少要选1个 + // 请返回子序列的最大累加和 + // arr : 数组 + // i : 当前来到i位置 + // pre : 前一个数字(i-1位置),当初选了没有 + // 如果pre == 0, 表示i-1位置的数字,当初没有选 + // 如果pre == 1, 表示i-1位置的数字,当初选了 + // 返回arr[i...]的子序列,最大累加和 + public static int maxSum(int[] arr, int i, int pre) { + if (i == arr.length) { + return 0; + } + // 可能性1 : 就是要选当前i位置的数 + int p1 = arr[i] + maxSum(arr, i + 1, 1); + // 可能性1 : 就是不选当前i位置的数 + int p2 = -1; + if (pre == 1) { // 只有前一个数字选了,当前才能不选 + p2 = maxSum(arr, i + 1, 0); + } + return Math.max(p1, p2); + } + + // 暴力方法 + // 为了验证 + public static int bestMedian1(int[] arr) { + int[] path = new int[arr.length]; + return process(arr, 0, true, path, 0); + } + + public static int process(int[] arr, int i, boolean pre, int[] path, int size) { + if (i == arr.length) { + if (size == 0) { + return 0; + } + int[] sort = new int[size]; + for (int j = 0; j < size; j++) { + sort[j] = path[j]; + } + Arrays.sort(sort); + return sort[(sort.length - 1) / 2]; + } else { + path[size] = arr[i]; + int ans = process(arr, i + 1, true, path, size + 1); + if (pre) { + ans = Math.max(ans, process(arr, i + 1, false, path, size)); + } + return ans; + } + } + + // 正式方法 + // 时间复杂度O(N*logN) + public static int bestMedian2(int[] arr) { + int n = arr.length; + int[] sort = new int[n]; + for (int i = 0; i < n; i++) { + sort[i] = arr[i]; + } + Arrays.sort(sort); +// int[] arr = { 5, 3, 6, 2, 9, 7 }; +// int[] sort = { 2, 3, 5, 6, 7, 9 }; +// 0 1 2 3 4 5 +// l r + int l = 0; + int r = n - 1; + int m = 0; + int ans = -1; + int[] help = new int[n]; + int[][] dp = new int[n + 1][2]; + while (l <= r) { + m = (l + r) / 2; + if (maxSum(arr, help, dp, sort[m], n) > 0) { + ans = sort[m]; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + // 如果中位数定成median, + // 如果任意相邻的两数,至少选一个,来生成序列 + // 所有这样的序列中, + // 到底有没有一个序列,其中>= median的数字,能达到一半以上 + public static int maxSum(int[] arr, int[] help, int[][] dp, int median, int n) { + for (int i = 0; i < n; i++) { + help[i] = arr[i] >= median ? 1 : -1; + } + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = help[i] + dp[i + 1][1]; + dp[i][1] = Math.max(help[i] + dp[i + 1][1], dp[i + 1][0]); + } + return dp[0][1]; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * v); + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + // 2标杆! + // test = 1, 1, 1, 1, 1, 1 -> 6 + // 3标杆! + // test = 1, 1, 1,-1, 1, 1 -> 5 + // 5标杆! + // test = 1,-1, 1,-1, 1, 1 -> 4 + // 6标杆! + // test = -1,-1, 1,-1, 1, 1 -> 2 + // 7标杆! + // test = -1,-1,-1,-1, 1, 1 -> 0 + + + +// // 5 6 9 -> 6 +// // 5 3 6 2 9 7 -> 2 3 5 6 7 9 -> 5 +// // 3 2 7 -> 2 3 7 -> 3 +// int[] test = { 5, 3, 6, 2, 9, 7 }; +// int[] sort = { 2, 3, 5, 6, 7, 9 }; +// int len = test.length; +// int[] help = new int[len]; +// int[][] dp = new int[len + 1][2]; +// System.out.println(maxSum(test, help, dp, 5, len)); +// System.out.println(maxSum(test, help, dp, 7, len)); +// System.out.println(maxSum(test, help, dp, 6, len)); + int N = 20; + int V = 1000; + int testTimes = 5000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int[] arr = randomArray(n, V); + int ans1 = bestMedian1(arr); + int ans2 = bestMedian2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + for (int num : arr) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + int n = 100000; + int v = 50000000; + System.out.println("数组长度 : " + n); + System.out.println("数值范围 : " + v); + int[] arr = randomArray(n, v); + long start = System.currentTimeMillis(); + bestMedian2(arr); + long end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + "毫秒"); + System.out.println("性能测试结束"); + + } + +} diff --git a/算法周更班/class_2022_10_2_week/Code03_FindMazeMinPath.java b/算法周更班/class_2022_10_2_week/Code03_FindMazeMinPath.java new file mode 100644 index 0000000..b19d75e --- /dev/null +++ b/算法周更班/class_2022_10_2_week/Code03_FindMazeMinPath.java @@ -0,0 +1,128 @@ +package class_2022_10_2_week; + +// 定义一个二维数组N*M,比如5*5数组下所示: +// 0, 1, 0, 0, 0, +// 0, 1, 1, 1, 0, +// 0, 0, 0, 0, 0, +// 0, 1, 1, 1, 0, +// 0, 0, 0, 1, 0, +// 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路 +// 只能横着走或竖着走,不能斜着走 +// 要求编程序找出从左上角到右下角距离最短的路线 +// 测试链接 : https://www.nowcoder.com/practice/cf24906056f4488c9ddb132f317e03bc +// 请同学们务必参考如下代码中关于输入、输出的处理 +// 这是输入输出处理效率很高的写法 +// 提交以下的code,提交时请把主类名改成"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.PriorityQueue; + +public class Code03_FindMazeMinPath { + + 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; + int[][] map = new int[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + in.nextToken(); + map[i][j] = (int) in.nval; + } + } + ArrayList ans = dijkstra(n, m, map); + for (int i = ans.size() - 1; i >= 0; i--) { + out.println("(" + ans.get(i)[0] + "," + ans.get(i)[1] + ")"); + } + out.flush(); + } + } + + // n : n行 + // m : m列 + // map : + // 0 1 1 1 + // 0 0 0 1 + // 1 1 0 1 + // 0 0 0 0 + // list = [0,0] , [1,0], [1,1]...[3,3] + // [3,3] -> [0,0] + public static ArrayList dijkstra(int n, int m, int[][] map) { + // (a,b) -> (c,d) + // last[c][d][0] = a + // last[c][d][1] = b + // 从哪到的当前(c,d) + int[][][] last = new int[n][m][2]; + // int[] arr = {c,d,w} + // 0 1 距离 + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[2] - b[2]); + boolean[][] visited = new boolean[n][m]; + heap.add(new int[] { 0, 0, 0 }); + ArrayList ans = new ArrayList<>(); + while (!heap.isEmpty()) { + int[] cur = heap.poll(); + int x = cur[0]; + int y = cur[1]; + int w = cur[2]; + if (x == n - 1 && y == m - 1) { + break; + } + if (visited[x][y]) { + continue; + } + // (x,y)这个点 + visited[x][y] = true; + add(x, y, x - 1, y, w, n, m, map, visited, heap, last); + add(x, y, x + 1, y, w, n, m, map, visited, heap, last); + add(x, y, x, y - 1, w, n, m, map, visited, heap, last); + add(x, y, x, y + 1, w, n, m, map, visited, heap, last); + } + int x = n - 1; + int y = m - 1; + while (x != 0 || y != 0) { + ans.add(new int[] { x, y }); + int lastX = last[x][y][0]; + int lastY = last[x][y][1]; + x = lastX; + y = lastY; + } + ans.add(new int[] { 0, 0 }); + return ans; + } + // 当前是从(x,y) -> (i,j) + // 左上角 -> (x,y) , 距离是w + // 左上角 -> (x,y) -> (i,j) w+1 + // 行一共有n行,0~n-1有效 + // 列一共有m行,0~m-1有效 + // map[i][j] == 1,不能走!是障碍! + // map[i][j] == 0,能走!是路! + // 把记录加入到堆里,所以得有heap + // last[i][j][0] = x + // last[i][j][1] = y + public static void add(int x, int y, + int i, int j, int w, + int n, int m, + int[][] map, + boolean[][] visited, + PriorityQueue heap, + int[][][] last) { + if (i >= 0 && i < n + && j >= 0 && j < m + && map[i][j] == 0 + && !visited[i][j]) { + heap.add(new int[] { i, j, w + 1 }); + last[i][j][0] = x; + last[i][j][1] = y; + } + } + +} \ No newline at end of file diff --git a/算法周更班/class_2022_10_2_week/Code04_ShortestPathToGetAllKeys.java b/算法周更班/class_2022_10_2_week/Code04_ShortestPathToGetAllKeys.java new file mode 100644 index 0000000..ff69468 --- /dev/null +++ b/算法周更班/class_2022_10_2_week/Code04_ShortestPathToGetAllKeys.java @@ -0,0 +1,131 @@ +package class_2022_10_2_week; + +import java.util.PriorityQueue; + +// 来自Airbnb、Uber +// 给定一个二维网格 grid ,其中: +// '.' 代表一个空房间 +// '#' 代表一堵 +// '@' 是起点 +// 小写字母代表钥匙 +// 大写字母代表锁 +// 我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间 +// 我们不能在网格外面行走,也无法穿过一堵墙 +// 如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。 +// 假设 k 为 钥匙/锁 的个数,且满足 1 <= k <= 6, +// 字母表中的前 k 个字母在网格中都有自己对应的一个小写和一个大写字母 +// 换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁 +// 另外,代表钥匙和锁的字母互为大小写并按字母顺序排列 +// 返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。 +// 测试链接:https://leetcode.cn/problems/shortest-path-to-get-all-keys +public class Code04_ShortestPathToGetAllKeys { + + // "@....#" + // "..b..B" + // + // @ . . . . # + // . . B . . B + public int shortestPathAllKeys(String[] grid) { + int n = grid.length; + char[][] map = new char[n][]; + for (int i = 0; i < grid.length; i++) { + map[i] = grid[i].toCharArray(); + } + int m = map[0].length; + return dijkstra(map, n, m); + } + + public static int dijkstra(char[][] map, int n, int m) { + int startX = 0; + int startY = 0; + int keys = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (map[i][j] == '@') { + startX = i; + startY = j; + } + if (map[i][j] >= 'a' && map[i][j] <= 'z') { + keys++; + } + } + } + // 如果有4把钥匙 + // limit = 0000..00001111 + // 如果有5把钥匙 + // limit = 0000..00011111 + // 也就是说,所有钥匙都凑齐的状态,就是limit + int limit = (1 << keys) - 1; + // 用堆来维持走过的点(dijkstra标准操作) + // 维持的信息是一个个小的4维数组,arr + // arr[0] : 当前来到的x坐标 + // arr[1] : 当前来到的y坐标 + // arr[2] : 当前收集到的钥匙状态 + // arr[3] : 从出发点到当前的距离 + // 堆根据距离的从小到大组织,距离小根堆 + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[3] - b[3]); + boolean[][][] visited = new boolean[n][m][1 << keys]; + // startX, startY, 000000 + heap.add(new int[] { startX, startY, 0, 0 }); + while (!heap.isEmpty()) { + int[] cur = heap.poll(); + int x = cur[0]; + int y = cur[1]; + int s = cur[2]; + int w = cur[3]; + if (s == limit) { + return w; + } + if (visited[x][y][s]) { + continue; + } + visited[x][y][s] = true; + add(x - 1, y, s, w, n, m, map, visited, heap); + add(x + 1, y, s, w, n, m, map, visited, heap); + add(x, y - 1, s, w, n, m, map, visited, heap); + add(x, y + 1, s, w, n, m, map, visited, heap); + } + return -1; + } + + // 当前是由(a,b,s) -> (x,y,状态?) + // w ,从最开始到达(a,b,s)这个点的距离 -> w+1 + // n,m 固定参数,防止越界 + // map 地图 + // visited 访问过的点,不要再加入到堆里去! + // heap, 堆! + public static void add( + int x, int y, int s, + int w, int n, int m, + char[][] map, boolean[][][] visited, + PriorityQueue heap) { + if (x < 0 || x == n || y < 0 || y == m || map[x][y] == '#') { + return; + } + if (map[x][y] >= 'A' && map[x][y] <= 'Z') { // 锁! + // B -> 00000010 + // dcba + // x,y,状 = x,y,s + // s == 00001000 + // dcba + // A s & (1 << 0) != 0 + // B s & (1 << 1) != 0 + // D s & (1 << 3) != 0 + // + if (!visited[x][y][s] && (s & (1 << (map[x][y] - 'A'))) != 0) { + heap.add(new int[] { x, y, s, w + 1 }); + } + } else { // 不是锁! + // 要么是钥匙 a b c + // 要么是空房间 . + // 要么是初始位置 @ + if (map[x][y] >= 'a' && map[x][y] <= 'z') { + s |= 1 << (map[x][y] - 'a'); + } + if (!visited[x][y][s]) { + heap.add(new int[] { x, y, s, w + 1 }); + } + } + } + +} diff --git a/算法周更班/class_2022_10_2_week/Code05_SoldierFindEnemy.java b/算法周更班/class_2022_10_2_week/Code05_SoldierFindEnemy.java new file mode 100644 index 0000000..daef7a4 --- /dev/null +++ b/算法周更班/class_2022_10_2_week/Code05_SoldierFindEnemy.java @@ -0,0 +1,202 @@ +package class_2022_10_2_week; + +import java.util.PriorityQueue; + +// 来自华为 +// 给定一个N*M的二维矩阵,只由字符'O'、'X'、'S'、'E'组成 +// 'O'表示这个地方是可通行的平地 +// 'X'表示这个地方是不可通行的障碍 +// 'S'表示这个地方有一个士兵,全图保证只有一个士兵 +// 'E'表示这个地方有一个敌人,全图保证只有一个敌人 +// 士兵可以在上、下、左、右四个方向上移动 +// 走到相邻的可通行的平地上,走一步耗费a个时间单位 +// 士兵从初始地点行动时,不管去哪个方向,都不用耗费转向的代价 +// 但是士兵在行动途中,如果需要转向,需要额外再付出b个时间单位 +// 返回士兵找到敌人的最少时间 +// 如果因为障碍怎么都找不到敌人,返回-1 +// 1 <= N,M <= 1000 +// 1 <= a,b <= 100000 +// 只会有一个士兵、一个敌人 +public class Code05_SoldierFindEnemy { + + // 暴力dfs + // 为了验证 + public static int minCost1(char[][] map, int a, int b) { + int n = map.length; + int m = map[0].length; + int startX = 0; + int startY = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (map[i][j] == 'S') { + startX = i; + startY = j; + } + } + } + boolean[][][] visited = new boolean[n][m][4]; + int p1 = f(map, startX, startY, 0, a, b, visited); + int p2 = f(map, startX, startY, 1, a, b, visited); + int p3 = f(map, startX, startY, 2, a, b, visited); + int p4 = f(map, startX, startY, 3, a, b, visited); + int ans = Math.min(Math.min(p1, p2), Math.min(p3, p4)); + return ans == Integer.MAX_VALUE ? -1 : (ans - a); + } + + public static int f(char[][] map, int si, int sj, int d, int a, int b, boolean[][][] visited) { + if (si < 0 || si == map.length || sj < 0 || sj == map[0].length || map[si][sj] == 'X' || visited[si][sj][d]) { + return Integer.MAX_VALUE; + } + if (map[si][sj] == 'E') { + return a; + } + visited[si][sj][d] = true; + int p0 = f(map, si - 1, sj, 0, a, b, visited); + int p1 = f(map, si + 1, sj, 1, a, b, visited); + int p2 = f(map, si, sj - 1, 2, a, b, visited); + int p3 = f(map, si, sj + 1, 3, a, b, visited); + if (d != 0 && p0 != Integer.MAX_VALUE) { + p0 += b; + } + if (d != 1 && p1 != Integer.MAX_VALUE) { + p1 += b; + } + if (d != 2 && p2 != Integer.MAX_VALUE) { + p2 += b; + } + if (d != 3 && p3 != Integer.MAX_VALUE) { + p3 += b; + } + int ans = Math.min(Math.min(p0, p1), Math.min(p2, p3)); + ans = ans == Integer.MAX_VALUE ? ans : (ans + a); + visited[si][sj][d] = false; + return ans; + } + + // 正式方法 + // Dijkstra算法 + public static int minCost2(char[][] map, int a, int b) { + int n = map.length; + int m = map[0].length; + int startX = 0; + int startY = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (map[i][j] == 'S') { + startX = i; + startY = j; + } + } + } + PriorityQueue heap = new PriorityQueue<>((x, y) -> x[3] - y[3]); + // (startX, startY) + heap.add(new int[] { startX, startY, 0, 0 }); + heap.add(new int[] { startX, startY, 1, 0 }); + heap.add(new int[] { startX, startY, 2, 0 }); + heap.add(new int[] { startX, startY, 3, 0 }); + // (i,j,朝向) + boolean[][][] visited = new boolean[n][m][4]; + int ans = -1; + while (!heap.isEmpty()) { + int[] cur = heap.poll(); +// int x = cur[0]; +// int y = cur[1]; +// int 朝向 = cur[2]; +// int 代价 = cur[3]; + if (visited[cur[0]][cur[1]][cur[2]]) { + continue; + } + if (map[cur[0]][cur[1]] == 'E') { + ans = cur[3]; + break; + } + visited[cur[0]][cur[1]][cur[2]] = true; + add(cur[0] - 1, cur[1], 0, cur[2], cur[3], a, b, map, visited, heap); + add(cur[0] + 1, cur[1], 1, cur[2], cur[3], a, b, map, visited, heap); + add(cur[0], cur[1] - 1, 2, cur[2], cur[3], a, b, map, visited, heap); + add(cur[0], cur[1] + 1, 3, cur[2], cur[3], a, b, map, visited, heap); + } + return ans; + } + + // 从(x,y, preD) -> (i,j,d) + // 走格子的代价a + // 转向的代价是b + // preC + a + public static void add( + int i, int j, int d, + int preD, int preC, + int a, int b, + char[][] map, boolean[][][] visited, + PriorityQueue heap) { + if (i < 0 || i == map.length + || j < 0 || j == map[0].length + || map[i][j] == 'X' + || visited[i][j][d]) { + return; + } + int cost = preC + a; + if (d != preD) { + cost += b; + } + heap.add(new int[] { i, j, d, cost }); + } + + // 为了测试 + public static char[][] randomMap(int n, int m) { + char[][] map = new char[n][m]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + map[i][j] = Math.random() < 0.5 ? 'O' : 'X'; + } + } + int si = (int) (Math.random() * n); + int sj = (int) (Math.random() * m); + map[si][sj] = 'S'; + int ei, ej; + do { + ei = (int) (Math.random() * n); + ej = (int) (Math.random() * m); + } while (ei == si && ej == sj); + map[ei][ej] = 'E'; + return map; + } + + public static void main(String[] args) { + int n = 3; + int m = 4; + int v = 10; + System.out.println("功能测试开始"); + for (int i = 0; i < 2000; i++) { + char[][] map = randomMap(n, m); + int a = (int) (Math.random() * v) + 1; + int b = (int) (Math.random() * v) + 1; + int ans1 = minCost1(map, a, b); + int ans2 = minCost2(map, a, b); + if (ans1 != ans2) { + System.out.println("出错了"); + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + n = 1000; + m = 1000; + v = 100000; + int a = (int) (Math.random() * v) + 1; + int b = (int) (Math.random() * v) + 1; + char[][] map = randomMap(n, m); + System.out.println("数据规模 : " + n + " * " + m); + System.out.println("通行代价 : " + a); + System.out.println("转向代价 : " + b); + long start = System.currentTimeMillis(); + minCost2(map, a, b); + long end = System.currentTimeMillis(); + System.out.println("运行时间 : " + (end - start) + "毫秒"); + System.out.println("功能测试结束"); + + } + +} diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 7231c28..348c91b 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -2114,4 +2114,81 @@ n <= 1000 +第044节 2022年10月第2周流行算法题目解析 + +来自小红书 +小A认为如果在数组中有一个数出现了至少k次 +且这个数是该数组的众数,即出现次数最多的数之一 +那么这个数组被该数所支配 +显然当k比较大的时候,有些数组不被任何数所支配 +现在小A拥有一个长度为n的数组,她想知道内部有多少个区间是被某个数支配的 +2 <= k <= n <= 100000 +1 <= 数组的值 <= n + +来自京东 +实习岗位笔试题 +给定一个数组arr,长度为n +任意相邻的两个数里面至少要有一个被选出来,组成子序列,才是合法的! +求所有可能的合法子序列中,最大中位数是多少 +中位数的定义为上中位数 +[1, 2, 3, 4]的上中位数是2 +[1, 2, 3, 4, 5]的上中位数是3 +2 <= n <= 10^5 +1 <= arr[i] <= 10^9 +我写的帖子解答 : https://www.mashibing.com/question/detail/34771 + +定义一个二维数组N*M,比如5*5数组下所示: +0, 1, 0, 0, 0, +0, 1, 1, 1, 0, +0, 0, 0, 0, 0, +0, 1, 1, 1, 0, +0, 0, 0, 1, 0, +它表示一个迷宫,其中的1表示墙壁,0表示可以走的路 +只能横着走或竖着走,不能斜着走 +要求编程序找出从左上角到右下角距离最短的路线 +测试链接 : https://www.nowcoder.com/practice/cf24906056f4488c9ddb132f317e03bc + +来自Airbnb、Uber +给定一个二维网格 grid ,其中: +'.' 代表一个空房间 +'#' 代表一堵 +'@' 是起点 +小写字母代表钥匙 +大写字母代表锁 +我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间 +我们不能在网格外面行走,也无法穿过一堵墙 +如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。 +假设 k 为 钥匙/锁 的个数,且满足 1 <= k <= 6, +字母表中的前 k 个字母在网格中都有自己对应的一个小写和一个大写字母 +换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁 +另外,代表钥匙和锁的字母互为大小写并按字母顺序排列 +返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。 +测试链接:https://leetcode.cn/problems/shortest-path-to-get-all-keys + +来自华为 +给定一个N*M的二维矩阵,只由字符'O'、'X'、'S'、'E'组成 +'O'表示这个地方是可通行的平地 +'X'表示这个地方是不可通行的障碍 +'S'表示这个地方有一个士兵,全图保证只有一个士兵 +'E'表示这个地方有一个敌人,全图保证只有一个敌人 +士兵可以在上、下、左、右四个方向上移动 +走到相邻的可通行的平地上,走一步耗费a个时间单位 +士兵从初始地点行动时,不管去哪个方向,都不用耗费转向的代价 +但是士兵在行动途中,如果需要转向,需要额外再付出b个时间单位 +返回士兵找到敌人的最少时间 +如果因为障碍怎么都找不到敌人,返回-1 +1 <= N,M <= 1000 +1 <= a,b <= 100000 +只会有一个士兵、一个敌人 + + + + + + + + + + +