From e8d2eae9e79fd51dc40d29d6d6907ac667bd4505 Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Wed, 19 Jul 2023 22:16:11 +0800 Subject: [PATCH] modify code --- ...SmallestRectangleEnclosingBlackPixels.java | 115 +++++++++++++++ .../Code02_SentenceSimilarityIII.java | 32 +++++ .../Code03_CanChangeMoneyNumbers.java | 131 ++++++++++++++++++ .../Code04_DinnerPlateStacks.java | 110 +++++++++++++++ ...owestCommonAncestorOfBinarySearchTree.java | 31 +++++ ...养的大厂算法面试题(正在直播) | 60 ++++++++ 6 files changed, 479 insertions(+) create mode 100644 算法周更班/class_2023_07_3_week/Code01_SmallestRectangleEnclosingBlackPixels.java create mode 100644 算法周更班/class_2023_07_3_week/Code02_SentenceSimilarityIII.java create mode 100644 算法周更班/class_2023_07_3_week/Code03_CanChangeMoneyNumbers.java create mode 100644 算法周更班/class_2023_07_3_week/Code04_DinnerPlateStacks.java create mode 100644 算法周更班/class_2023_07_3_week/Code05_LowestCommonAncestorOfBinarySearchTree.java diff --git a/算法周更班/class_2023_07_3_week/Code01_SmallestRectangleEnclosingBlackPixels.java b/算法周更班/class_2023_07_3_week/Code01_SmallestRectangleEnclosingBlackPixels.java new file mode 100644 index 0000000..e5433ab --- /dev/null +++ b/算法周更班/class_2023_07_3_week/Code01_SmallestRectangleEnclosingBlackPixels.java @@ -0,0 +1,115 @@ +package class_2023_07_3_week; + +// 先来一个最近学员在国外面试的题 +// 有一个由x轴和y轴组成的坐标系 +// "y下"和"y上"表示一条无限延伸的道路,"y下"表示这个道路的下限,"y上"表示这个道路的上限 +// 给定一批长方形,每一个长方形有(x1, x2, y1, y2),4个坐标可以表示一个长方形 +// 判断这条道路整体是不是可以走通的 +// 以下为正式题目 +// 图片在计算机处理中往往是使用二维矩阵来表示的 +// 给你一个大小为 m x n 的二进制矩阵 image 表示一张黑白图片,0 代表白色像素,1 代表黑色像素 +// 黑色像素相互连接,也就是说,图片中只会有一片连在一块儿的黑色像素。像素点是水平或竖直方向连接的 +// 给你两个整数 x 和 y 表示某一个黑色像素的位置 +// 请你找出包含全部黑色像素的最小矩形(与坐标轴对齐),并返回该矩形的面积 +// 你必须设计并实现一个时间复杂度低于 O(m*n) 的算法来解决此问题。 +// 测试链接 : https://leetcode.cn/problems/smallest-rectangle-enclosing-black-pixels/ +public class Code01_SmallestRectangleEnclosingBlackPixels { + + public static int minArea(char[][] image, int x, int y) { + int left = left(image, y); + int right = right(image, y); + int up = up(image, x, left, right); + int down = down(image, x, left, right); + return (right - left + 1) * (down - up + 1); + } + + public static int left(char[][] image, int col) { + // .....左?....... col + int l = 0, r = col - 1, m, ans = col; + boolean find; + while (l <= r) { + m = (l + r) / 2; + find = false; + for (int i = 0; i < image.length; i++) { + if (image[i][m] == '1') { + find = true; + break; + } + } + if (find) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static int right(char[][] image, int col) { + int l = col + 1, r = image[0].length - 1, m, ans = col; + boolean find; + while (l <= r) { + m = (l + r) / 2; + find = false; + for (int i = 0; i < image.length; i++) { + if (image[i][m] == '1') { + find = true; + break; + } + } + if (find) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static int up(char[][] image, int row, int left, int right) { + int u = 0, d = row - 1, m, ans = row; + boolean find; + while (u <= d) { + m = (u + d) / 2; + find = false; + for (int i = left; i <= right; i++) { + if (image[m][i] == '1') { + find = true; + break; + } + } + if (find) { + ans = m; + d = m - 1; + } else { + u = m + 1; + } + } + return ans; + } + + public static int down(char[][] image, int row, int left, int right) { + int u = row + 1, d = image.length - 1, m, ans = row; + boolean find; + while (u <= d) { + m = (u + d) / 2; + find = false; + for (int i = left; i <= right; i++) { + if (image[m][i] == '1') { + find = true; + break; + } + } + if (find) { + ans = m; + u = m + 1; + } else { + d = m - 1; + } + } + return ans; + } + +} diff --git a/算法周更班/class_2023_07_3_week/Code02_SentenceSimilarityIII.java b/算法周更班/class_2023_07_3_week/Code02_SentenceSimilarityIII.java new file mode 100644 index 0000000..02df681 --- /dev/null +++ b/算法周更班/class_2023_07_3_week/Code02_SentenceSimilarityIII.java @@ -0,0 +1,32 @@ +package class_2023_07_3_week; + +// 一个句子是由一些单词与它们之间的单个空格组成 +// 且句子的开头和结尾没有多余空格 +// 比方说,"Hello World" ,"HELLO" ,"hello world hello world" 都是句子 +// 每个单词都 只 包含大写和小写英文字母 +// 如果两个句子 sentence1 和 sentence2 +// 可以通过往其中一个句子插入一个任意的句子(可以是空句子)而得到另一个句子 +// 那么我们称这两个句子是 相似的 +// 比方说,sentence1 = "Hello my name is Jane" +// 且 sentence2 = "Hello Jane" +// 我们可以往 sentence2 中 "Hello" 和 "Jane" 之间插入 "my name is" +// 得到 sentence1 +// 给你两个句子 sentence1 和 sentence2 +// 如果 sentence1 和 sentence2 是相似的,请你返回 true ,否则返回 false +// 测试链接 : https://leetcode.cn/problems/sentence-similarity-iii/ +public class Code02_SentenceSimilarityIII { + + public static boolean areSentencesSimilar(String s1, String s2) { + String[] w1 = s1.split(" "); + String[] w2 = s2.split(" "); + int i = 0, j = 0, n1 = w1.length, n2 = w2.length; + while (i < n1 && i < n2 && w1[i].equals(w2[i])) { + i++; + } + while (n1 - j > i && n2 - j > i && w1[n1 - 1 - j].equals(w2[n2 - 1 - j])) { + j++; + } + return i + j == Math.min(n1, n2); + } + +} diff --git a/算法周更班/class_2023_07_3_week/Code03_CanChangeMoneyNumbers.java b/算法周更班/class_2023_07_3_week/Code03_CanChangeMoneyNumbers.java new file mode 100644 index 0000000..772206b --- /dev/null +++ b/算法周更班/class_2023_07_3_week/Code03_CanChangeMoneyNumbers.java @@ -0,0 +1,131 @@ +package class_2023_07_3_week; + +// 每一种货币都给定面值val[i],和拥有的数量cnt[i] +// 想知道目前拥有的货币,在钱数为1、2、3...m时,能找零成功的钱数有多少 +// 也就是说当钱数的范围是1~m,返回这个范围上有多少可以找零成功的钱数 +// 比如只有3元的货币,数量是5张 +// m = 10 +// 那么在1~10范围上,只有钱数是3、6、9时,可以成功找零 +// 所以返回3,表示有3种钱数可以找零成功 +// 测试链接 : http://poj.org/problem?id=1742 +// 请同学们务必参考如下代码中关于输入、输出的处理 +// 这是输入输出处理效率很高的写法 +// 提交以下的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.Arrays; + +public class Code03_CanChangeMoneyNumbers { + + public static int MAXN = 101; + + public static int[] val = new int[MAXN]; + + public static int[] cnt = new int[MAXN]; + + public static int MAXM = 100001; + + public static boolean[] dp = new boolean[MAXM]; + + public static int n, m; + + 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; + if (n != 0 || m != 0) { + for (int i = 1; i <= n; i++) { + in.nextToken(); + val[i] = (int) in.nval; + } + for (int i = 1; i <= n; i++) { + in.nextToken(); + cnt[i] = (int) in.nval; + } + out.println(compute()); + } else { + out.flush(); + } + } + } + + public static int compute() { + Arrays.fill(dp, 1, m + 1, false); + dp[0] = true; + // 每一种货币,遍历 + for (int i = 1; i <= n; i++) { + if (cnt[i] == 1) { + // // 等于当前货币就1张 + for (int j = m; j >= val[i]; j--) { + if (dp[j - val[i]]) { + dp[j] = true; + } + } + } else if (val[i] * cnt[i] > m) { + // 等于当前货币无限张 + for (int j = val[i]; j <= m; j++) { + if (dp[j - val[i]]) { + dp[j] = true; + } + } + } else { + // 既不是1张,也不是无限张 + for (int mod = 0; mod < val[i]; mod++) { + // val[i] = 3元 + // 0 : .... + // 1 : .... + // 2 : .... + int trueCnt = 0; + // 0 : m元 m-3元 m-6元 m-9元 + // 1 : m-1元 m-4元 m-7元 m-10元 + // 2 : m-2元 + // + // 3元,4张 + // + // + // 9 12 【15 18 21 24】 + // + for (int j = m - mod, size = 0; j >= 0 && size <= cnt[i]; j -= val[i], size++) { + trueCnt += dp[j] ? 1 : 0; + } +// // 9 12 【15 18 21 24】 + // 9 【12 15 18 21】 24 + // 每次窗口出去一个下标 + // 进来一个下标 + for (int j = m - mod, l = j - val[i] * (cnt[i] + 1); j >= 1; j -= val[i], l -= val[i]) { + // dp[j] = 上一行的值 + // dp[j] 更新成 本行的值 + + if (dp[j]) { + trueCnt--; + } else { + if (trueCnt != 0) { + dp[j] = true; + } + } + if (l >= 0) { + trueCnt += dp[l] ? 1 : 0; + } + } + } + } + } + int ans = 0; + for (int i = 1; i <= m; i++) { + if (dp[i]) { + ans++; + } + } + return ans; + } + +} diff --git a/算法周更班/class_2023_07_3_week/Code04_DinnerPlateStacks.java b/算法周更班/class_2023_07_3_week/Code04_DinnerPlateStacks.java new file mode 100644 index 0000000..b172f3d --- /dev/null +++ b/算法周更班/class_2023_07_3_week/Code04_DinnerPlateStacks.java @@ -0,0 +1,110 @@ +package class_2023_07_3_week; + +import java.util.ArrayList; +import java.util.PriorityQueue; + +// 我们把无限数量的栈排成一行,按从左到右的次序从 0 开始编号 +// 每个栈的的最大容量 capacity 都相同。实现一个叫「餐盘」的类 DinnerPlates +// DinnerPlates(int capacity) - 给出栈的最大容量 capacity +// void push(int val) 将给出的正整数 val 推入 从左往右第一个 没有满的栈 +// int pop() 返回 从右往左第一个 非空栈顶部的值,并将其从栈中删除 +// 如果所有的栈都是空的,请返回 -1。 +// int popAtStack(int index) - 返回编号 index 的栈顶部的值,并将其从栈中删除 +// 如果编号 index 的栈是空的,请返回 -1。 +// 测试链接 : https://leetcode.cn/problems/dinner-plate-stacks/ +public class Code04_DinnerPlateStacks { + + class DinnerPlates { + // 根据题目数据量,leetcode升级数据,改大 + private final int N = 100001; + private int capacity; + // 所有栈的结构 + private ArrayList> stacks = new ArrayList<>(); + // 每个栈里有多少数字 + private int[] cnt = new int[N + 1]; + // 曾经满了,然后又因为popAtStack方法变得不满的栈 + // 编号放在这个堆里,小根堆 + // 可以理解为空洞栈组成的堆 + private PriorityQueue heap = new PriorityQueue<>(); + // 拥有数字的栈中,最右栈的编号 + private int rightStack; + + public DinnerPlates(int cap) { + capacity = cap; + rightStack = 0; + } + + // 从右往左拿数, + public int pop() { + while (cnt[rightStack] == 0 && rightStack > 0) { + rightStack--; + } + if (cnt[rightStack] == 0) { + // 来到0号站,还没遇到数字 + return -1; + } + return stacks.get(rightStack).remove(--cnt[rightStack]); + } + + public int popAtStack(int index) { + if (cnt[index] == 0) { + return -1; + } + int ans = stacks.get(index).remove(cnt[index] - 1); + if (cnt[index] == capacity) { + heap.add(index); + } + cnt[index]--; + return ans; + } + + // push、pop、popAt + + + public void push(int val) { + if (heap.isEmpty()) { + // 没有空洞 + // 0.....rightstack + if (cnt[rightStack] == capacity && rightStack < N) { + rightStack++; + } + if (stacks.size() == rightStack) { + stacks.add(new ArrayList<>()); + } + stacks.get(rightStack).add(val); + cnt[rightStack]++; + } else { + // 有空洞 + // rightStack往左来到该去的位置,往左缩 + while (cnt[rightStack] == 0 && rightStack > 0) { + rightStack--; + } + if (heap.peek() >= rightStack) { + // 所有空洞,没用了 + while (!heap.isEmpty()) { + heap.poll(); + } + // 0.....rightStack + if (cnt[rightStack] == capacity) { + rightStack++; + } + if (stacks.size() == rightStack) { + stacks.add(new ArrayList<>()); + } + stacks.get(rightStack).add(val); + cnt[rightStack]++; + } else { + // 不是所有空洞都失效 + int cur = heap.peek(); + cnt[cur]++; + stacks.get(cur).add(val); + if (cnt[cur] == capacity) { + heap.poll(); + } + } + } + } + + } + +} diff --git a/算法周更班/class_2023_07_3_week/Code05_LowestCommonAncestorOfBinarySearchTree.java b/算法周更班/class_2023_07_3_week/Code05_LowestCommonAncestorOfBinarySearchTree.java new file mode 100644 index 0000000..1539b67 --- /dev/null +++ b/算法周更班/class_2023_07_3_week/Code05_LowestCommonAncestorOfBinarySearchTree.java @@ -0,0 +1,31 @@ +package class_2023_07_3_week; + +// 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先 +// 测试链接 : https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/ +public class Code05_LowestCommonAncestorOfBinarySearchTree { + + // 不提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + } + + // 提交以下的方法 + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + // root从上到下 + // 如果先遇到了p,说明p是答案 + // 如果先遇到了q,说明q是答案 + // 如果root在p和q的值之间,不用管p和q谁大谁小,只要root在中间,那么此时的root就是答案 + // 如果root在p和q的值的左侧,那么root往右移动 + // 如果root在p和q的值的右侧,那么root往左移动 + while (root.val != p.val && root.val != q.val) { + if (Math.min(p.val, q.val) < root.val && root.val < Math.max(p.val, q.val)) { + break; + } + root = root.val < Math.min(p.val, q.val) ? root.right : root.left; + } + return root; + } + +} diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index dcd15f1..f4a4c7d 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -4130,6 +4130,66 @@ forceField[i] = [x,y,side] +第077节 2023年7月3周流行算法题目解析 + +先来一个最近学员在国外面试的题 +有一个由x轴和y轴组成的坐标系 +"y下"和"y上"表示一条无限延伸的道路,"y下"表示这个道路的下限,"y上"表示这个道路的上限 +给定一批长方形,每一个长方形有(x1, x2, y1, y2),4个坐标可以表示一个长方形 +判断这条道路整体是不是可以走通的 +以下为正式题目 +图片在计算机处理中往往是使用二维矩阵来表示的 +给你一个大小为 m x n 的二进制矩阵 image 表示一张黑白图片,0 代表白色像素,1 代表黑色像素 +黑色像素相互连接,也就是说,图片中只会有一片连在一块儿的黑色像素。像素点是水平或竖直方向连接的 +给你两个整数 x 和 y 表示某一个黑色像素的位置 +请你找出包含全部黑色像素的最小矩形(与坐标轴对齐),并返回该矩形的面积 +你必须设计并实现一个时间复杂度低于 O(m*n) 的算法来解决此问题。 +测试链接 : https://leetcode.cn/problems/smallest-rectangle-enclosing-black-pixels/ + +一个句子是由一些单词与它们之间的单个空格组成 +且句子的开头和结尾没有多余空格 +比方说,"Hello World" ,"HELLO" ,"hello world hello world" 都是句子 +每个单词都 只 包含大写和小写英文字母 +如果两个句子 sentence1 和 sentence2 +可以通过往其中一个句子插入一个任意的句子(可以是空句子)而得到另一个句子 +那么我们称这两个句子是 相似的 +比方说,sentence1 = "Hello my name is Jane" +且 sentence2 = "Hello Jane" +我们可以往 sentence2 中 "Hello" 和 "Jane" 之间插入 "my name is" +得到 sentence1 +给你两个句子 sentence1 和 sentence2 +如果 sentence1 和 sentence2 是相似的,请你返回 true ,否则返回 false +测试链接 : https://leetcode.cn/problems/sentence-similarity-iii/ + +每一种货币都给定面值val[i],和拥有的数量cnt[i] +想知道目前拥有的货币,在钱数为1、2、3...m时,能找零成功的钱数有多少 +也就是说当钱数的范围是1~m,返回这个范围上有多少可以找零成功的钱数 +比如只有3元的货币,数量是5张 +m = 10 +那么在1~10范围上,只有钱数是3、6、9时,可以成功找零 +所以返回3,表示有3种钱数可以找零成功 +测试链接 : http://poj.org/problem?id=1742 + +我们把无限数量的栈排成一行,按从左到右的次序从 0 开始编号 +每个栈的的最大容量 capacity 都相同。实现一个叫「餐盘」的类 DinnerPlates +DinnerPlates(int capacity) - 给出栈的最大容量 capacity +void push(int val) 将给出的正整数 val 推入 从左往右第一个 没有满的栈 +int pop() 返回 从右往左第一个 非空栈顶部的值,并将其从栈中删除 + 如果所有的栈都是空的,请返回 -1。 +int popAtStack(int index) - 返回编号 index 的栈顶部的值,并将其从栈中删除 + 如果编号 index 的栈是空的,请返回 -1。 +测试链接 : https://leetcode.cn/problems/dinner-plate-stacks/ + +给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先 +测试链接 : https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/ + + + + + + + +