modify code

master
algorithmzuo 2 years ago
parent 489d28f3b5
commit 6a93df978f

@ -0,0 +1,23 @@
package class_2023_03_3_week;
// 给你一个 非递减 的正整数数组 nums 和整数 K
// 判断该数组是否可以被分成一个或几个 长度至少 为 K 的 不相交的递增子序列
// 测试链接 : https://leetcode.cn/problems/divide-array-into-increasing-sequences/
public class Code01_DivideArrayIntoIncreasingSequences {
public static boolean canDivideIntoSubsequences(int[] nums, int k) {
int cnt = 1;
int maxCnt = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i - 1] != nums[i]) {
maxCnt = Math.max(maxCnt, cnt);
cnt = 1;
} else {
cnt++;
}
}
maxCnt = Math.max(maxCnt, cnt);
return nums.length / maxCnt >= k;
}
}

@ -0,0 +1,119 @@
package class_2023_03_3_week;
import java.util.ArrayList;
import java.util.Arrays;
// 来自学员问题
// 真实大厂笔试题
// 给定一个数组arr长度为n
// 再给定一个数字k表示一定要将arr划分成k个集合
// 每个数字只能进一个集合
// 返回每个集合内部的平均值都累加起来最小的值
// 平均值向下取整
// 1 <= n <= 10^5
// 0 <= arr[i] <= 10^5
// 1 <= k <= n
public class Code02_SplitToSubsetMakeMinAverageSum {
// 暴力方法
// 为了验证
public static int minAverageSum1(int[] arr, int k) {
if (arr.length < k) {
return -1;
}
ArrayList<Info> sets = new ArrayList<>();
for (int i = 0; i < k; i++) {
sets.add(new Info(0, 0));
}
return process(arr, 0, k, sets);
}
// 暴力方法
// 为了验证
public static class Info {
public int sum;
public int cnt;
public Info(int s, int c) {
sum = s;
cnt = c;
}
}
// 暴力方法
// 为了验证
public static int process(int[] arr, int i, int k, ArrayList<Info> sets) {
if (i == arr.length) {
int ans = 0;
for (Info set : sets) {
if (set.cnt == 0) {
return Integer.MAX_VALUE;
} else {
ans += set.sum / set.cnt;
}
}
return ans;
} else {
int ans = Integer.MAX_VALUE;
int cur = arr[i];
for (int j = 0; j < k; j++) {
sets.get(j).sum += cur;
sets.get(j).cnt += 1;
ans = Math.min(ans, process(arr, i + 1, k, sets));
sets.get(j).sum -= cur;
sets.get(j).cnt -= 1;
}
return ans;
}
}
// 正式方法
// 时间复杂度O(N * logN)
public static int minAverageSum2(int[] arr, int k) {
if (arr.length < k) {
return -1;
}
Arrays.sort(arr);
int ans = 0;
for (int i = 0; i < k - 1; i++) {
ans += arr[i];
}
int sum = 0;
for (int i = k - 1; i < arr.length; i++) {
sum += arr[i];
}
ans += sum / (arr.length - k + 1);
return ans;
}
// 生成随机数组
// 为了测试
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) {
int N = 8;
int V = 10000;
int testTimes = 2000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[] arr = randomArray(n, V);
int k = (int) (Math.random() * n) + 1;
int ans1 = minAverageSum1(arr, k);
int ans2 = minAverageSum2(arr, k);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,47 @@
package class_2023_03_3_week;
import java.util.HashMap;
// 给你一个正整数数组 nums请你移除 最短 子数组(可以为 空)
// 使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。
// 请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。
// 子数组 定义为原数组中连续的一组元素。
// 测试链接 : https://leetcode.cn/problems/make-sum-divisible-by-p/
public class Code03_MakeSumDivisibleByP {
public int minSubarray(int[] nums, int p) {
int n = nums.length;
// 求出整体的余数
int allMod = 0;
for (int num : nums) {
allMod = (allMod + num) % p;
}
if (allMod == 0) {
return 0;
}
// 记录前缀和的某个余数,最晚出现的位置
// 看课!然后看接下来的代码
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0, -1);
int ans = Integer.MAX_VALUE;
int curMod = 0, find;
for (int i = 0; i < n; i++) {
// 0...i 累加和的余数
curMod = (curMod + nums[i]) % p;
// 如果p = 7整体余数2当前余数5那么找之前的部分余数是3
// 如果p = 7整体余数2当前余数1那么找之前的部分余数是6
// 整体变成下面的公式,可以自己带入各种情况验证
find = (curMod - allMod + p) % p;
if (map.containsKey(find)) {
if (i != n - 1 || map.get(find) != -1) {
// 防止删掉整体!
// ...i(n-1)
ans = Math.min(ans, i - map.get(find));
}
}
map.put(curMod, i);
}
return ans == Integer.MAX_VALUE ? -1 : ans;
}
}

@ -0,0 +1,145 @@
package class_2023_03_3_week;
// 布尔表达式 是计算结果不是 true 就是 false 的表达式
// 有效的表达式需遵循以下约定:
// 't',运算结果为 true
// 'f',运算结果为 false
// '!(subExpr)',运算过程为对内部表达式 subExpr 进行 逻辑非NOT运算
// '&(subExpr1, subExpr2, ..., subExprn)'
// 运算过程为对 2 个或以上内部表达式
// subExpr1, subExpr2, ..., subExprn 进行 逻辑与AND运算
// '|(subExpr1, subExpr2, ..., subExprn)'
// 运算过程为对 2 个或以上内部表达式
// subExpr1, subExpr2, ..., subExprn 进行 逻辑或OR运算
// 给你一个以字符串形式表述的 布尔表达式 expression返回该式的运算结果。
// 题目测试用例所给出的表达式均为有效的布尔表达式,遵循上述约定。
// 测试链接 : https://leetcode.cn/problems/parsing-a-boolean-expression/
public class Code04_ParsingBooleanExpression {
public static boolean parseBoolExpr(String expression) {
return f(expression.toCharArray(), 0).ans;
}
public static class Info {
public boolean ans;
// 结束下标!
public int end;
public Info(boolean a, int e) {
ans = a;
end = e;
}
}
// 核心递归
public static Info f(char[] exp, int index) {
char judge = exp[index];
if (judge == 'f') {
return new Info(false, index);
} else if (judge == 't') {
return new Info(true, index);
} else {
// !
// &
// |
// 再说!
boolean ans;
// ! ( ?
// i i+1 i+2
// & ( ?
// i i+1 i+2
// | ( ?
// i i+1 i+2
index += 2;
if (judge == '!') {
// ! ( ?...... )
// i i+1 i+2
Info next = f(exp, index);
ans = !next.ans;
index = next.end + 1;
} else {
// &
// i
// judge == '&' 或者 judge == '|'
ans = judge == '&';
while (exp[index] != ')') {
if (exp[index] == ',') {
index++;
} else {
Info next = f(exp, index);
if (judge == '&') {
if (!next.ans) {
ans = false;
}
} else {
if (next.ans) {
ans = true;
}
}
index = next.end + 1;
}
}
}
return new Info(ans, index);
}
}
// public static Info f(char[] exp, int index) {
// char judge = exp[index];
// if (judge == 'f') {
// return new Info(false, index);
// } else if (judge == 't') {
// return new Info(true, index);
// } else {
// // !
// // &
// // |
// // 再说!
// boolean ans;
// // ! ( ?
// // i i+1 i+2
// // & ( ?
// // i i+1 i+2
// // | ( ?
// // i i+1 i+2
// index += 2;
// if (judge == '!') {
// // ! ( ?...... )
// // i i+1 i+2
// Info next = f(exp, index);
// ans = !next.ans;
// index = next.end + 1;
// } else if (judge == '&') {
// // & ( ?... , ?.... , ?.... )
// // i i+1 index
// ans = true;
// while (exp[index] != ')') {
// if (exp[index] == ',') {
// index++;
// } else {
// Info next = f(exp, index);
// if (!next.ans) {
// ans = false;
// }
// index = next.end + 1;
// }
// }
// } else {
// ans = false;
// while (exp[index] != ')') {
// if (exp[index] == ',') {
// index++;
// } else {
// Info next = f(exp, index);
// if (next.ans) {
// ans = true;
// }
// index = next.end + 1;
// }
// }
// }
// return new Info(ans, index);
// }
// }
}

@ -0,0 +1,298 @@
package class_2023_03_3_week;
import java.util.Arrays;
// 来自学员问题,大厂笔试面经帖子
// 假设一共有M个车库编号1~M时间点从早到晚是从1~T
// 一共有N个记录每一条记录如下{a, b, c}
// 表示一辆车在b时间点进入a车库在c时间点从a车库出去
// 一共有K个查询每个查询只有一个数字X表示请问在X时刻
// 有多少个车库包含车的数量>=3请返回K个查询的答案
// 1 <= M, N, K <= 10^5
// 1 <= T <= 10^9
public class Code05_QueryGreaterThanOrEqualTo3Stores {
// 暴力方法
// 为了验证
public static int[] getAns1(int m, int[][] records, int[] queries) {
int maxT = 0;
for (int[] r : records) {
maxT = Math.max(maxT, Math.max(r[1], r[2]));
}
for (int t : queries) {
maxT = Math.max(maxT, t);
}
int[][] stores = new int[m + 1][maxT + 1];
for (int[] record : records) {
int s = record[0];
int l = record[1];
int r = record[2] - 1;
for (int i = l; i <= r; i++) {
stores[s][i]++;
}
}
int k = queries.length;
int[] ans = new int[k];
for (int i = 0; i < k; i++) {
int curAns = 0;
for (int j = 1; j <= m; j++) {
if (stores[j][queries[i]] >= 3) {
curAns++;
}
}
ans[i] = curAns;
}
return ans;
}
// 正式方法
// O((N + K)*log(N + K))
public static int[] getAns2(int m, int[][] records, int[] queries) {
int n = records.length;
int k = queries.length;
// n*2 + k
int tn = (n << 1) + k;
int[] times = new int[tn + 1];
int ti = 1;
for (int[] record : records) {
times[ti++] = record[1];
times[ti++] = record[2] - 1;
}
for (int query : queries) {
times[ti++] = query;
}
Arrays.sort(times);
for (int[] record : records) {
record[1] = rank(times, record[1]);
record[2] = rank(times, record[2] - 1);
}
for (int i = 0; i < k; i++) {
queries[i] = rank(times, queries[i]);
}
// 所有记录,根据车库编号排序,相同车库的记录就在一起了
// record 车库 入库时间 出库时间-1
// 0 1 2
Arrays.sort(records, (a, b) -> a[0] - b[0]);
SegmentTree st = new SegmentTree(tn);
for (int l = 0; l < n;) {
int r = l;
while (r < n && records[l][0] == records[r][0]) {
r++;
}
// 同一个车库的记录records[l.....r-1] l...... l.......
countRange(records, l, r - 1, st);
l = r;
}
int[] ans = new int[k];
for (int i = 0; i < k; i++) {
ans[i] = st.query(queries[i]);
}
return ans;
}
// records[l.....r]
// 上面的记录都属于当前的车库
// 找到哪些时间段,车的数量是 >= 3的
// 改写线段树!
public static void countRange(int[][] records, int l, int r, SegmentTree st) {
int n = r - l + 1;
int[][] help = new int[n << 1][2];
int size = 0;
for (int i = l; i <= r; i++) {
if (records[i][1] <= records[i][2]) {
help[size][0] = records[i][1];
help[size++][1] = 1;
help[size][0] = records[i][2];
help[size++][1] = -1;
}
}
Arrays.sort(help, 0, size, (a, b) -> a[0] != b[0] ? (a[0] - b[0]) : (b[1] - a[1]));
int count = 0;
int start = -1;
for (int i = 0; i < size; i++) {
int point = help[i][0];
int status = help[i][1];
if (status == 1) {
if (++count >= 3) {
start = start == -1 ? point : start;
}
} else {
if (start != -1 && start <= point) {
st.add(start, point);
}
if (--count >= 3) {
start = point + 1;
} else {
start = -1;
}
}
}
}
// 所有的时间点在sorted数组中排好序了
// 给我一个其中的时间点v
// 返回,离散化之后的编号!
public static int rank(int[] sorted, int v) {
int l = 1;
int r = sorted.length;
int m = 0;
int ans = 0;
while (l <= r) {
m = (l + r) / 2;
if (sorted[m] >= v) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
// 线段树
// 维持任何时刻存车数量>=3的车库有几个
// 支持范围时刻的增加
// 支持单点时刻的查询
public static class SegmentTree {
private int tn;
private int[] sum;
private int[] lazy;
public SegmentTree(int n) {
tn = n;
sum = new int[(tn + 1) << 2];
lazy = new int[(tn + 1) << 2];
}
private void pushUp(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
private void pushDown(int rt, int ln, int rn) {
if (lazy[rt] != 0) {
lazy[rt << 1] += lazy[rt];
sum[rt << 1] += lazy[rt] * ln;
lazy[rt << 1 | 1] += lazy[rt];
sum[rt << 1 | 1] += lazy[rt] * rn;
lazy[rt] = 0;
}
}
// l...r范围上每个时刻对应的数值+1
public void add(int l, int r) {
add(l, r, 1, tn, 1);
}
private void add(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
sum[rt] += r - l + 1;
lazy[rt] += 1;
return;
}
int mid = (l + r) >> 1;
pushDown(rt, mid - l + 1, r - mid);
if (L <= mid) {
add(L, R, l, mid, rt << 1);
}
if (R > mid) {
add(L, R, mid + 1, r, rt << 1 | 1);
}
pushUp(rt);
}
// 查询单点时刻存车数量>=3的车库有几个
public int query(int index) {
return query(index, 1, tn, 1);
}
private int query(int index, int l, int r, int rt) {
if (l == r) {
return sum[rt];
}
int m = (l + r) >> 1;
pushDown(rt, m - l + 1, r - m);
int ans = 0;
if (index <= m) {
ans = query(index, l, m, rt << 1);
} else {
ans = query(index, m + 1, r, rt << 1 | 1);
}
return ans;
}
}
// 为了测试
public static int[][] randomRecords(int n, int m, int t) {
int[][] records = new int[n][3];
for (int i = 0; i < n; i++) {
records[i][0] = (int) (Math.random() * m) + 1;
int a = (int) (Math.random() * t) + 1;
int b = (int) (Math.random() * t) + 1;
records[i][1] = Math.min(a, b);
records[i][2] = Math.max(a, b);
}
return records;
}
// 为了测试
public static int[] randomQueries(int k, int t) {
int[] queries = new int[k];
for (int i = 0; i < k; i++) {
queries[i] = (int) (Math.random() * t) + 1;
}
return queries;
}
// 为了测试
public static boolean same(int[] ans1, int[] ans2) {
for (int i = 0; i < ans1.length; i++) {
if (ans1[i] != ans2[i]) {
return false;
}
}
return true;
}
// 为了测试
public static void main(String[] args) {
int M = 20;
int N = 300;
int K = 500;
int T = 5000;
int testTimes = 5000;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int m = (int) (Math.random() * M) + 1;
int n = (int) (Math.random() * N) + 1;
int k = (int) (Math.random() * K) + 1;
int t = (int) (Math.random() * T) + 1;
int[][] records = randomRecords(n, m, t);
int[] queries = randomQueries(k, t);
int[] ans1 = getAns1(m, records, queries);
int[] ans2 = getAns2(m, records, queries);
if (!same(ans1, ans2)) {
System.out.println("出错了!");
break;
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
int m = 100000;
int n = 100000;
int k = 100000;
int t = 1000000000;
int[][] records = randomRecords(n, m, t);
int[] queries = randomQueries(k, t);
System.out.println("车库规模 : " + m);
System.out.println("记录规模 : " + n);
System.out.println("查询条数 : " + k);
System.out.println("时间范围 : " + t);
long start = System.currentTimeMillis();
getAns2(m, records, queries);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -3208,6 +3208,60 @@ R(e_1 + e_2) = {a + b for (a, b) in R(e_1) × R(e_2)}
第062节 2023年3月第3周流行算法题目解析
给你一个 非递减 的正整数数组 nums 和整数 K
判断该数组是否可以被分成一个或几个 长度至少 为 K 的 不相交的递增子序列
测试链接 : https://leetcode.cn/problems/divide-array-into-increasing-sequences/
来自学员问题
真实大厂笔试题
给定一个数组arr长度为n
再给定一个数字k表示一定要将arr划分成k个集合
每个数字只能进一个集合
返回每个集合内部的平均值都累加起来最小的值
平均值向下取整
1 <= n <= 10^5
0 <= arr[i] <= 10^5
1 <= k <= n
给你一个正整数数组 nums请你移除 最短 子数组(可以为 空)
使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。
请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。
子数组 定义为原数组中连续的一组元素。
测试链接 : https://leetcode.cn/problems/make-sum-divisible-by-p/
布尔表达式 是计算结果不是 true 就是 false 的表达式
有效的表达式需遵循以下约定:
't',运算结果为 true
'f',运算结果为 false
'!(subExpr)',运算过程为对内部表达式 subExpr 进行 逻辑非NOT运算
'&(subExpr1, subExpr2, ..., subExprn)'
运算过程为对 2 个或以上内部表达式
subExpr1, subExpr2, ..., subExprn 进行 逻辑与AND运算
'|(subExpr1, subExpr2, ..., subExprn)'
运算过程为对 2 个或以上内部表达式
subExpr1, subExpr2, ..., subExprn 进行 逻辑或OR运算
给你一个以字符串形式表述的 布尔表达式 expression返回该式的运算结果。
题目测试用例所给出的表达式均为有效的布尔表达式,遵循上述约定。
测试链接 : https://leetcode.cn/problems/parsing-a-boolean-expression/
来自学员问题,大厂笔试面经帖子
假设一共有M个车库编号1~M时间点从早到晚是从1~T
一共有N个记录每一条记录如下{a, b, c}
表示一辆车在b时间点进入a车库在c时间点从a车库出去
一共有K个查询每个查询只有一个数字X表示请问在X时刻
有多少个车库包含车的数量>=3请返回K个查询的答案
1 <= M, N, K <= 10^5
1 <= T <= 10^9

Loading…
Cancel
Save