modify code

master
algorithmzuo 3 years ago
parent 0d01ef1a35
commit fe69f9aacd

@ -0,0 +1,22 @@
package 03.mca_02;
import java.util.Arrays;
import java.util.PriorityQueue;
public class Code01_CoverMax {
public static int maxCover(int[][] m) {
Arrays.sort(m, (a, b) -> (a[0] - b[0]));
PriorityQueue<Integer> heap = new PriorityQueue<>();
int max = 0;
for (int[] line : m) {
while (!heap.isEmpty() && heap.peek() <= line[0]) {
heap.poll();
}
heap.add(line[1]);
max = Math.max(max, heap.size());
}
return max;
}
}

@ -0,0 +1,22 @@
package 03.mca_02;
import java.util.PriorityQueue;
public class Code02_LessMoneySplitGold {
public static int lessMoney(int[] arr) {
PriorityQueue<Integer> pQ = new PriorityQueue<>();
for (int i = 0; i < arr.length; i++) {
pQ.add(arr[i]);
}
int sum = 0;
int cur = 0;
while (pQ.size() > 1) {
cur = pQ.poll() + pQ.poll();
sum += cur;
pQ.add(cur);
}
return sum;
}
}

@ -0,0 +1,54 @@
package 03.mca_02;
import java.util.Arrays;
import java.util.PriorityQueue;
// 有 n 名工人。 给定两个数组 quality 和 wage
// 其中quality[i] 表示第 i 名工人的工作质量,其最低期望工资为 wage[i] 。
// 现在我们想雇佣 k 名工人组成一个工资组。在雇佣 一组 k 名工人时,
// 我们必须按照下述规则向他们支付工资:
// 对工资组中的每名工人,应当按其工作质量与同组其他工人的工作质量的比例来支付工资。
// 工资组中的每名工人至少应当得到他们的最低期望工资。
// 给定整数 k ,返回 组成满足上述条件的付费群体所需的最小金额
// 测试链接 : https://leetcode.cn/problems/minimum-cost-to-hire-k-workers/
public class Code03_MinimumCostToHireKWorkers {
public static class Employee {
public double rubbishDegree;
public int quality;
public Employee(int w, int q) {
rubbishDegree = (double) w / (double) q;
quality = q;
}
}
public double mincostToHireWorkers(int[] quality, int[] wage, int k) {
int n = quality.length;
Employee[] employees = new Employee[n];
for (int i = 0; i < n; i++) {
employees[i] = new Employee(wage[i], quality[i]);
}
Arrays.sort(employees, (a, b) -> a.rubbishDegree <= b.rubbishDegree ? -1 : 1);
PriorityQueue<Integer> minTops = new PriorityQueue<Integer>((a, b) -> b - a);
double ans = Double.MAX_VALUE;
for (int i = 0, qualitySum = 0; i < n; i++) {
int curQuality = employees[i].quality;
if (minTops.size() < k) { // 堆没满
qualitySum += curQuality;
minTops.add(curQuality);
if (minTops.size() == k) {
ans = Math.min(ans, qualitySum * employees[i].rubbishDegree);
}
} else {
if (minTops.peek() > curQuality) {
qualitySum += curQuality - minTops.poll();
minTops.add(curQuality);
ans = Math.min(ans, qualitySum * employees[i].rubbishDegree);
}
}
}
return ans;
}
}

@ -1,138 +0,0 @@
package 03.mca_02;
public class Code03_SmallerEqualBigger {
public static class Node {
public int value;
public Node next;
public Node(int data) {
this.value = data;
}
}
public static Node listPartition1(Node head, int pivot) {
if (head == null) {
return head;
}
Node cur = head;
int i = 0;
while (cur != null) {
i++;
cur = cur.next;
}
Node[] nodeArr = new Node[i];
i = 0;
cur = head;
for (i = 0; i != nodeArr.length; i++) {
nodeArr[i] = cur;
cur = cur.next;
}
arrPartition(nodeArr, pivot);
for (i = 1; i != nodeArr.length; i++) {
nodeArr[i - 1].next = nodeArr[i];
}
nodeArr[i - 1].next = null;
return nodeArr[0];
}
public static void arrPartition(Node[] nodeArr, int pivot) {
int small = -1;
int big = nodeArr.length;
int index = 0;
while (index != big) {
if (nodeArr[index].value < pivot) {
swap(nodeArr, ++small, index++);
} else if (nodeArr[index].value == pivot) {
index++;
} else {
swap(nodeArr, --big, index);
}
}
}
public static void swap(Node[] nodeArr, int a, int b) {
Node tmp = nodeArr[a];
nodeArr[a] = nodeArr[b];
nodeArr[b] = tmp;
}
public static Node listPartition2(Node head, int pivot) {
Node sH = null; // small head
Node sT = null; // small tail
Node eH = null; // equal head
Node eT = null; // equal tail
Node mH = null; // big head
Node mT = null; // big tail
Node next = null; // save next node
// every node distributed to three lists
while (head != null) {
next = head.next;
head.next = null;
if (head.value < pivot) {
if (sH == null) {
sH = head;
sT = head;
} else {
sT.next = head;
sT = head;
}
} else if (head.value == pivot) {
if (eH == null) {
eH = head;
eT = head;
} else {
eT.next = head;
eT = head;
}
} else {
if (mH == null) {
mH = head;
mT = head;
} else {
mT.next = head;
mT = head;
}
}
head = next;
}
// 小于区域的尾巴,连等于区域的头,等于区域的尾巴连大于区域的头
if (sT != null) { // 如果有小于区域
sT.next = eH;
eT = eT == null ? sT : eT; // 下一步谁去连大于区域的头谁就变成eT
}
// 下一步一定是需要用eT 去接 大于区域的头
// 有等于区域eT -> 等于区域的尾结点
// 无等于区域eT -> 小于区域的尾结点
// eT 尽量不为空的尾巴节点
if (eT != null) { // 如果小于区域和等于区域,不是都没有
eT.next = mH;
}
return sH != null ? sH : (eH != null ? eH : mH);
}
public static void printLinkedList(Node node) {
System.out.print("Linked List: ");
while (node != null) {
System.out.print(node.value + " ");
node = node.next;
}
System.out.println();
}
public static void main(String[] args) {
Node head1 = new Node(7);
head1.next = new Node(9);
head1.next.next = new Node(1);
head1.next.next.next = new Node(8);
head1.next.next.next.next = new Node(5);
head1.next.next.next.next.next = new Node(2);
head1.next.next.next.next.next.next = new Node(5);
printLinkedList(head1);
// head1 = listPartition1(head1, 4);
head1 = listPartition2(head1, 5);
printLinkedList(head1);
}
}

@ -1,6 +1,6 @@
package 03.mca_02;
public class Code07_FindFirstIntersectNode {
public class Code04_FindFirstIntersectNode {
public static class Node {
public int value;

@ -1,7 +1,7 @@
package 03.mca_02;
// 测试链接https://leetcode.com/problems/add-two-numbers/
public class Code01_AddTwoNumbers {
public class Code05_AddTwoNumbers {
// 不要提交这个类
public static class ListNode {

@ -2,7 +2,7 @@ package 第03期.mca_02;
import java.util.Stack;
public class Code02_IsPalindromeList {
public class Code06_IsPalindromeList {
public static class Node {
public int value;

@ -3,7 +3,7 @@ package 第03期.mca_02;
import java.util.HashMap;
// 测试链接 : https://leetcode.com/problems/copy-list-with-random-pointer/
public class Code04_CopyListWithRandom {
public class Code07_CopyListWithRandom {
public static class Node {
int val;

@ -1,155 +0,0 @@
package 03.mca_02;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Code08_CoverMax {
public static int maxCover1(int[][] lines) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 0; i < lines.length; i++) {
min = Math.min(min, lines[i][0]);
max = Math.max(max, lines[i][1]);
}
int cover = 0;
for (double p = min + 0.5; p < max; p += 1) {
int cur = 0;
for (int i = 0; i < lines.length; i++) {
if (lines[i][0] < p && lines[i][1] > p) {
cur++;
}
}
cover = Math.max(cover, cur);
}
return cover;
}
public static int maxCover2(int[][] m) {
Line[] lines = new Line[m.length];
for (int i = 0; i < m.length; i++) {
lines[i] = new Line(m[i][0], m[i][1]);
}
Arrays.sort(lines, new StartComparator());
// 小根堆,每一条线段的结尾数值,使用默认的
PriorityQueue<Integer> heap = new PriorityQueue<>();
int max = 0;
for (int i = 0; i < lines.length; i++) {
// lines[i] -> cur 在黑盒中,把<=cur.start 东西都弹出
while (!heap.isEmpty() && heap.peek() <= lines[i].start) {
heap.poll();
}
heap.add(lines[i].end);
max = Math.max(max, heap.size());
}
return max;
}
public static class Line {
public int start;
public int end;
public Line(int s, int e) {
start = s;
end = e;
}
}
public static class EndComparator implements Comparator<Line> {
@Override
public int compare(Line o1, Line o2) {
return o1.end - o2.end;
}
}
// 和maxCover2过程是一样的
// 只是代码更短
// 不使用类定义的写法
public static int maxCover3(int[][] m) {
// m是二维数组可以认为m内部是一个一个的一维数组
// 每一个一维数组就是一个对象,也就是线段
// 如下的code就是根据每一个线段的开始位置排序
// 比如, m = { {5,7}, {1,4}, {2,6} } 跑完如下的code之后变成{ {1,4}, {2,6}, {5,7} }
Arrays.sort(m, (a, b) -> (a[0] - b[0]));
// 准备好小根堆,和课堂的说法一样
PriorityQueue<Integer> heap = new PriorityQueue<>();
int max = 0;
for (int[] line : m) {
while (!heap.isEmpty() && heap.peek() <= line[0]) {
heap.poll();
}
heap.add(line[1]);
max = Math.max(max, heap.size());
}
return max;
}
// for test
public static int[][] generateLines(int N, int L, int R) {
int size = (int) (Math.random() * N) + 1;
int[][] ans = new int[size][2];
for (int i = 0; i < size; i++) {
int a = L + (int) (Math.random() * (R - L + 1));
int b = L + (int) (Math.random() * (R - L + 1));
if (a == b) {
b = a + 1;
}
ans[i][0] = Math.min(a, b);
ans[i][1] = Math.max(a, b);
}
return ans;
}
public static class StartComparator implements Comparator<Line> {
@Override
public int compare(Line o1, Line o2) {
return o1.start - o2.start;
}
}
public static void main(String[] args) {
Line l1 = new Line(4, 9);
Line l2 = new Line(1, 4);
Line l3 = new Line(7, 15);
Line l4 = new Line(2, 4);
Line l5 = new Line(4, 6);
Line l6 = new Line(3, 7);
// 底层堆结构heap
PriorityQueue<Line> heap = new PriorityQueue<>(new StartComparator());
heap.add(l1);
heap.add(l2);
heap.add(l3);
heap.add(l4);
heap.add(l5);
heap.add(l6);
while (!heap.isEmpty()) {
Line cur = heap.poll();
System.out.println(cur.start + "," + cur.end);
}
System.out.println("test begin");
int N = 100;
int L = 0;
int R = 200;
int testTimes = 200000;
for (int i = 0; i < testTimes; i++) {
int[][] lines = generateLines(N, L, R);
int ans1 = maxCover1(lines);
int ans2 = maxCover2(lines);
int ans3 = maxCover3(lines);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("Oops!");
}
}
System.out.println("test end");
}
}

@ -3,7 +3,7 @@
package 03.mca_02;
// 测试链接https://leetcode.com/problems/reverse-nodes-in-k-group/
public class Code05_ReverseNodesInKGroup {
public class Code08_ReverseNodesInKGroup {
// 不要提交这个类
public static class ListNode {

@ -1,79 +0,0 @@
package 03.mca_02;
import java.util.PriorityQueue;
public class Code09_LessMoneySplitGold {
// 纯暴力!
public static int lessMoney1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
return process(arr, 0);
}
// 等待合并的数都在arr里pre之前的合并行为产生了多少总代价
// arr中只剩一个数字的时候停止合并返回最小的总代价
public static int process(int[] arr, int pre) {
if (arr.length == 1) {
return pre;
}
int ans = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
ans = Math.min(ans, process(copyAndMergeTwo(arr, i, j), pre + arr[i] + arr[j]));
}
}
return ans;
}
public static int[] copyAndMergeTwo(int[] arr, int i, int j) {
int[] ans = new int[arr.length - 1];
int ansi = 0;
for (int arri = 0; arri < arr.length; arri++) {
if (arri != i && arri != j) {
ans[ansi++] = arr[arri];
}
}
ans[ansi] = arr[i] + arr[j];
return ans;
}
public static int lessMoney2(int[] arr) {
PriorityQueue<Integer> pQ = new PriorityQueue<>();
for (int i = 0; i < arr.length; i++) {
pQ.add(arr[i]);
}
int sum = 0;
int cur = 0;
while (pQ.size() > 1) {
cur = pQ.poll() + pQ.poll();
sum += cur;
pQ.add(cur);
}
return sum;
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * (maxValue + 1));
}
return arr;
}
public static void main(String[] args) {
int testTime = 100000;
int maxSize = 6;
int maxValue = 1000;
for (int i = 0; i < testTime; i++) {
int[] arr = generateRandomArray(maxSize, maxValue);
if (lessMoney1(arr) != lessMoney2(arr)) {
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}

@ -4,7 +4,7 @@ import java.util.Comparator;
import java.util.PriorityQueue;
// 测试链接https://leetcode.com/problems/merge-k-sorted-lists/
public class Code06_MergeKSortedLists {
public class Code09_MergeKSortedLists {
public static class ListNode {
public int val;

@ -0,0 +1,80 @@
package class_2023_02_3_week;
// 如果一个正整数自身是回文数,而且它也是一个回文数的平方,那么我们称这个数为超级回文数。
// 现在,给定两个正整数 L 和 R (以字符串形式表示),
// 返回包含在范围 [L, R] 中的超级回文数的数目。
// 测试链接 : https://leetcode.cn/problems/super-palindromes/
public class Code01_SuperPalindromes {
// L ... R "123213213" ~ "31283712710381299823"
public static int superpalindromesInRange(String left, String right) {
long l = Long.valueOf(left);
long r = Long.valueOf(right);
// 限制是根据开方的范围
long limit = (long) Math.sqrt((double) r);
int cnt = 0;
long seed = 1;
long enlarge = 0;
do {
// seed = 123
// 123321
enlarge = enlarge2(seed);
if (isValid(enlarge * enlarge, l, r)) {
cnt++;
}
// seed = 123
// 12321
enlarge = enlarge1(seed);
if (isValid(enlarge * enlarge, l, r)) {
cnt++;
}
seed++;
} while (enlarge < limit);
return cnt;
}
public static long enlarge1(long seed) {
long ans = seed;
seed /= 10;
while (seed != 0) {
ans = ans * 10 + seed % 10;
seed /= 10;
}
return ans;
}
public static long enlarge2(long seed) {
long ans = seed;
while (seed != 0) {
ans = ans * 10 + seed % 10;
seed /= 10;
}
return ans;
}
public static boolean isValid(long ans, long l, long r) {
return isPalindrome(ans) && ans >= l && ans <= r;
}
public static boolean isPalindrome(long n) {
// n = 3721837
// help = 1000000
long help = 1;
while (n / help >= 10) {
help *= 10;
}
// n = 3 72183 7
// help = 1000000
// 左 : n / help = 3
// 右 : n % 10 = 7
while (n != 0) {
if (n / help != n % 10) {
return false;
}
n = (n % help) / 10;
help /= 100;
}
return true;
}
}

@ -0,0 +1,72 @@
package class_2023_02_3_week;
// 我们从二叉树的根节点 root 开始进行深度优先搜索。
// 在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度)
// 然后输出该节点的值。(如果节点的深度为 D则其直接子节点的深度为 D + 1
// 根节点的深度为 0
// 如果节点只有一个子节点,那么保证该子节点为左子节点
// 给出遍历输出 S还原树并返回其根节点 root。
// 测试链接 : https://leetcode.cn/problems/recover-a-tree-from-preorder-traversal/
public class Code02_RecoverTreeFromPreorderTraversal {
// 不提交这个类
public static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int v) {
val = v;
}
}
// 提交如下的代码
public static int MAXN = 2001;
public static int[] queue = new int[MAXN];
public static int l, r;
public static TreeNode recoverFromPreorder(String traversal) {
l = 0;
r = 0;
int number = 0;
int level = 0;
boolean pickLevel = true;
for (int i = 0; i < traversal.length(); i++) {
if (traversal.charAt(i) != '-') {
if (pickLevel) {
queue[r++] = level;
level = 0;
pickLevel = false;
}
number = number * 10 + traversal.charAt(i) - '0';
} else {
if (!pickLevel) {
queue[r++] = number;
number = 0;
pickLevel = true;
}
level++;
}
}
queue[r++] = number;
return f();
}
// 当前,消费的时间是 :
// 层 : queue[l]
// 节点值 : queue[l+1]
public static TreeNode f() {
int level = queue[l++];
TreeNode head = new TreeNode(queue[l++]);
if (l < r && queue[l] > level) {
head.left = f();
}
if (l < r && queue[l] > level) {
head.right = f();
}
return head;
}
}

@ -0,0 +1,65 @@
package class_2023_02_3_week;
import java.util.Arrays;
import java.util.HashMap;
// 给你一份工作时间表 hours上面记录着某一位员工每天的工作小时数。
// 我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。
// 所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
// 请你返回「表现良好时间段」的最大长度。
// 测试链接 : https://leetcode.cn/problems/longest-well-performing-interval/
public class Code03_LongestWellPerformingInterval {
// 哈希表
public static int longestWPI1(int[] hours) {
// key : 某个前缀和
// value : 这个前缀和最早出现的位置
HashMap<Integer, Integer> map = new HashMap<>();
// 0这个前缀和最早出现在哪一个数也没有的时候
map.put(0, -1);
int ans = 0;
int sum = 0;
for (int i = 0; i < hours.length; i++) {
sum += hours[i] > 8 ? 1 : -1;
if (sum > 0) {
// 0...i i+1
ans = i + 1;
} else {
// sum = -4
// -5最早出现在哪 j j+1...i
if (map.containsKey(sum - 1)) {
ans = Math.max(ans, i - map.get(sum - 1));
}
}
if (!map.containsKey(sum)) {
map.put(sum, i);
}
}
return ans;
}
// 数组替代哈希表
public static int longestWPI2(int[] hours) {
int n = hours.length;
int[] early = new int[(n << 1) + 1];
Arrays.fill(early, -2);
early[0 + n] = -1;
int ans = 0;
int sum = 0;
for (int i = 0; i < hours.length; i++) {
sum += hours[i] > 8 ? 1 : -1;
if (sum > 1) {
ans = i + 1;
} else {
if (sum - 1 + n >= 0 && early[sum - 1 + n] != -2) {
ans = Math.max(ans, i - early[sum - 1 + n]);
}
}
if (early[sum + n] == -2) {
early[sum + n] = i;
}
}
return ans;
}
}

@ -0,0 +1,236 @@
package class_2023_02_3_week;
import java.util.Map.Entry;
import java.util.TreeMap;
// 来自TikTok美国笔试
// 给定一个长度为N的一维数组scores, 代表0~N-1号员工的初始得分
// scores[i] = a, 表示i号员工一开始得分是a
// 给定一个长度为M的二维数组operations,
// operations[i] = {a, b, c}
// 表示第i号操作为 :
// 如果a==1, 表示将目前分数<b的所有员工分数改成bc这个值无用
// 如果a==2, 表示将编号为b的员工分数改成c
// 所有操作从0~M-1, 依次发生
// 返回一个长度为N的一维数组ans表示所有操作做完之后每个员工的得分是多少
// 1 <= N <= 10的6次方
// 1 <= M <= 10的6次方
// 0 <= 分数 <= 10的9次方
public class Code04_OperateScores {
// 暴力方法
// 为了验证
public static int[] operateScores1(int[] scores, int[][] operations) {
int n = scores.length;
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = scores[i];
}
for (int[] op : operations) {
if (op[0] == 1) {
for (int i = 0; i < n; i++) {
ans[i] = Math.max(ans[i], op[1]);
}
} else {
ans[op[1]] = op[2];
}
}
return ans;
}
// 正式方法
// 时间复杂度O((N + M) * logN)
public static int[] operateScores2(int[] scores, int[][] operations) {
int n = scores.length;
Node[] nodes = new Node[n];
// 得分 -> 桶
TreeMap<Integer, Bucket> scoreBucketMap = new TreeMap<>();
for (int i = 0; i < n; i++) {
// 0号人0号Node
// i号人i号Node
nodes[i] = new Node(i);
// 17分 -> 桶
if (!scoreBucketMap.containsKey(scores[i])) {
scoreBucketMap.put(scores[i], new Bucket());
}
scoreBucketMap.get(scores[i]).add(nodes[i]);
}
// m次操作
for (int[] op : operations) {
if (op[0] == 1) {
// op[] = {1, 70, X}
// < 70
// merge 进 70号桶
// <= 69 且最近的
// 13 45 53 65
// 65 -> 70 65X
// <= 69
// 13 45 53
// 53 -> 70 53X
// 13 45
// <= 69 记录没有了!
// <= 69 有没有记录!
Integer floorKey = scoreBucketMap.floorKey(op[1] - 1);
// 1)有 <= 69的记录 70号得分的桶却没有
if (floorKey != null && !scoreBucketMap.containsKey(op[1])) {
scoreBucketMap.put(op[1], new Bucket());
}
while (floorKey != null) {
scoreBucketMap.get(op[1]).merge(scoreBucketMap.get(floorKey));
scoreBucketMap.remove(floorKey);
floorKey = scoreBucketMap.floorKey(op[1] - 1);
}
} else {
// 2类型
// op = [2, 17号人改成80分]
// cur就是当前人的Node
Node cur = nodes[op[1]];
// 链接last cur next
// 把last 和 next 串起来cur去往新的桶
cur.conectLastNext();
if (!scoreBucketMap.containsKey(op[2])) {
scoreBucketMap.put(op[2], new Bucket());
}
scoreBucketMap.get(op[2]).add(cur);
}
}
int[] ans = new int[n];
for (Entry<Integer, Bucket> entry : scoreBucketMap.entrySet()) {
int score = entry.getKey();
Bucket bucket = entry.getValue();
Node cur = bucket.head.next;
while (cur != bucket.tail) {
ans[cur.index] = score;
cur = cur.next;
}
}
return ans;
}
// 桶得分在有序表里桶只作为有序表里的value不作为key
public static class Bucket {
// 注意!
// 头和尾都是假点!
// 头和尾中间的节点才是真实节点
// 头为假 ...中间节点才是有用的数据... 尾为假
// 为什么这么实现,很关键!
// 课上讲!
public Node head = null;
public Node tail = null;
public Bucket() {
head = new Node(-1);
tail = new Node(-1);
head.next = tail;
tail.last = head;
}
public void add(Node node) {
node.last = tail.last;
node.next = tail;
tail.last.next = node;
tail.last = node;
}
public void merge(Bucket join) {
if (join.head.next != join.tail) {
tail.last.next = join.head.next;
join.head.next.last = tail.last;
join.tail.last.next = tail;
tail.last = join.tail.last;
join.head.next = join.tail;
join.tail.last = join.head;
}
}
}
public static class Node {
public int index;
public Node last = null;
public Node next = null;
public Node(int i) {
index = i;
}
public void conectLastNext() {
last.next = next;
next.last = last;
}
}
// 为了测试
public static int[] randomSocres(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 int[][] randomOperations(int n, int m, int v) {
int[][] ans = new int[m][3];
for (int i = 0; i < m; i++) {
if (Math.random() < 0.5) {
ans[i][0] = 1;
ans[i][1] = (int) (Math.random() * v);
} else {
ans[i][0] = 2;
ans[i][1] = (int) (Math.random() * n);
ans[i][2] = (int) (Math.random() * v);
}
}
return ans;
}
// 为了测试
public static boolean isEqual(int[] arr1, int[] arr2) {
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
// 为了测试
public static void main(String[] args) {
int N = 1000;
int M = 1000;
int V = 100000;
int testTimes = 100;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int m = (int) (Math.random() * M) + 1;
int[] scores = randomSocres(n, V);
int[][] operations = randomOperations(n, m, V);
int[] ans1 = operateScores1(scores, operations);
int[] ans2 = operateScores2(scores, operations);
if (!isEqual(ans1, ans2)) {
System.out.println("出错了!");
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
int n = 1000000;
int m = 1000000;
int v = 1000000000;
int[] scores = randomSocres(n, v);
int[][] operations = randomOperations(n, m, v);
System.out.println("总人数 : " + n);
System.out.println("操作数 : " + n);
System.out.println("值范围 : " + v);
long start = System.currentTimeMillis();
operateScores2(scores, operations);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,256 @@
package class_2023_02_3_week;
import java.util.ArrayList;
import java.util.Arrays;
// 来自TikTok美国笔试
// 给定一个长度为N的数组arrarr[i]表示宝石的价值
// 你在某天遇到X价值的宝石
// X价值如果是所有剩余宝石价值中的最小值你会将该宝石送人
// X价值如果不是所有剩余宝石价值中的最小值你会将该宝石放到所有宝石的最后
// 返回把宝石都送人需要多少天
// 比如arr = [3,1,4,3,1,2]
// 在第1天你遇到了价值3的宝石但是3并不是所有剩余宝石的价值最小值
// 所以你把3放在了所有宝石的最后arr = [1,4,3,1,2,3]
// 在第2天你遇到了价值1的宝石1是所有剩余宝石的价值最小值
// 所以你把价值1的宝石送人arr = [4,3,1,2,3]
// 在第3天你把价值4的宝石放到最后arr = [3,1,2,3,4]
// 在第4天你把价值3的宝石放到最后arr = [1,2,3,4,3]
// 在第5天你送出了价值1的宝石arr = [2,3,4,3]
// 在第6天你送出了价值2的宝石arr = [3,4,3]
// 在第7天你送出了价值3的宝石arr = [4,3]
// 在第8天你把价值4的宝石放到最后arr = [3,4]
// 在第9天你送出了价值3的宝石arr = [4]
// 在第10天你送出了价值4的宝石宝石已经没有了
// 所以返回10
// 1 <= N <= 10的5次方
// 1 <= 宝石价值 <= 10的9次方
public class Code05_SendOutDiamondDays {
// 暴力方法
// 为了验证
public static int days1(int[] diamonds) {
ArrayList<Integer> arr = new ArrayList<>();
for (int num : diamonds) {
arr.add(num);
}
int ans = 0;
while (!arr.isEmpty()) {
ans++;
deal(arr);
}
return ans;
}
// 暴力方法
// 为了验证
public static void deal(ArrayList<Integer> arr) {
int head = arr.remove(0);
int min = head;
for (int i = 0; i < arr.size(); i++) {
min = Math.min(min, arr.get(i));
}
if (head > min) {
arr.add(head);
}
}
// 正式方法
// 时间复杂度O(N * (logN)的平方)
public static int days2(int[] diamonds) {
// n : 位置
int n = diamonds.length;
// 1 ~ n : 1
// 1 1 1 1 1 1 1
// 1 2 3 4 5 6 7
IndexTree it = new IndexTree(n);
// 7 6 2...
// 1 2 3....
SegmentTree st = new SegmentTree(diamonds);
int days = 0;
int find, start = 1;
while (it.sum(1, n) != 0) {
// start ..... find(后续....最小值,最左的位置)
find = find(st, start, n);
days += days(it, start, find, n);
// 1
// find
it.add(find, -1);
st.update(find, Integer.MAX_VALUE);
start = find;
}
return days;
}
public static int find(SegmentTree st, int start, int n) {
// start....n 左部分 1 ~ start-1 右
int l, r, min = st.min(1, n);
if (st.min(start, n) == min) {
l = start;
r = n;
} else {
l = 1;
r = start - 1;
}
int m, ans = -1;
while (l <= r) {
m = (l + r) / 2;
if (st.min(l, m) == min) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
public static int days(IndexTree sumIt, int start, int find, int n) {
if (start <= find) {
return sumIt.sum(start, find);
} else {
return sumIt.sum(start, n) + sumIt.sum(1, find);
}
}
// 支持查询累加和
public static class IndexTree {
private int[] tree;
private int n;
public IndexTree(int size) {
n = size;
tree = new int[n + 1];
for (int i = 1; i <= n; i++) {
add(i, 1);
}
}
private int sum(int i) {
int ret = 0;
while (i > 0) {
ret += tree[i];
i -= i & -i;
}
return ret;
}
public int sum(int l, int r) {
return sum(r) - sum(l - 1);
}
public void add(int i, int d) {
while (i <= n) {
tree[i] += d;
i += i & -i;
}
}
}
// 支持查询最小值
public static class SegmentTree {
private int n;
private int[] min;
public SegmentTree(int[] arr) {
n = arr.length;
min = new int[(n + 1) << 2];
Arrays.fill(min, Integer.MAX_VALUE);
for (int i = 1; i <= n; i++) {
update(i, arr[i - 1]);
}
}
public void update(int i, int v) {
update(i, i, v, 1, n, 1);
}
public int min(int l, int r) {
return min(l, r, 1, n, 1);
}
private void pushUp(int rt) {
min[rt] = Math.min(min[rt << 1], min[rt << 1 | 1]);
}
private void update(int L, int R, int C, int l, int r, int rt) {
if (L <= l && r <= R) {
min[rt] = C;
return;
}
int mid = (l + r) >> 1;
if (L <= mid) {
update(L, R, C, l, mid, rt << 1);
}
if (R > mid) {
update(L, R, C, mid + 1, r, rt << 1 | 1);
}
pushUp(rt);
}
private int min(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
return min[rt];
}
int mid = (l + r) >> 1;
int ans = Integer.MAX_VALUE;
if (L <= mid) {
ans = Math.min(ans, min(L, R, l, mid, rt << 1));
}
if (R > mid) {
ans = Math.min(ans, min(L, R, mid + 1, r, rt << 1 | 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) {
System.out.println("例子测试开始");
int[] arr = { 3, 1, 4, 3, 1, 2 };
System.out.println(days1(arr));
System.out.println(days2(arr));
System.out.println("例子测试结束");
int N = 100;
int V = 100000;
int testTimes = 1000;
System.out.println("随机测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[] diamonds = randomArray(n, V);
int ans1 = days1(diamonds);
int ans2 = days2(diamonds);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("随机测试结束");
System.out.println("性能测试开始");
int n = 100000;
int v = 1000000000;
int[] diamonds = randomArray(n, V);
System.out.println("宝石数量 : " + n);
System.out.println("价值范围 : " + v);
long start = System.currentTimeMillis();
days2(diamonds);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -2955,6 +2955,69 @@ initial 中每个整数都不同
第058节 2023年2月第3周流行算法题目解析
如果一个正整数自身是回文数,而且它也是一个回文数的平方,那么我们称这个数为超级回文数。
现在,给定两个正整数 L 和 R (以字符串形式表示),
返回包含在范围 [L, R] 中的超级回文数的数目。
测试链接 : https://leetcode.cn/problems/super-palindromes/
我们从二叉树的根节点 root 开始进行深度优先搜索。
在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度)
然后输出该节点的值。(如果节点的深度为 D则其直接子节点的深度为 D + 1
根节点的深度为 0
如果节点只有一个子节点,那么保证该子节点为左子节点
给出遍历输出 S还原树并返回其根节点 root。
测试链接 : https://leetcode.cn/problems/recover-a-tree-from-preorder-traversal/
给你一份工作时间表 hours上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。
测试链接 : https://leetcode.cn/problems/longest-well-performing-interval/
来自TikTok美国笔试
给定一个长度为N的一维数组scores, 代表0~N-1号员工的初始得分
scores[i] = a, 表示i号员工一开始得分是a
给定一个长度为M的二维数组operations,
operations[i] = {a, b, c}
表示第i号操作为 :
如果a==1, 表示将目前分数<b的所有员工分数改成bc这个值无用
如果a==2, 表示将编号为b的员工分数改成c
所有操作从0~M-1, 依次发生
返回一个长度为N的一维数组ans表示所有操作做完之后每个员工的得分是多少
1 <= N <= 10的6次方
1 <= M <= 10的6次方
0 <= 分数 <= 10的9次方
来自TikTok美国笔试
给定一个长度为N的数组arrarr[i]表示宝石的价值
你在某天遇到X价值的宝石
X价值如果是所有剩余宝石价值中的最小值你会将该宝石送人
X价值如果不是所有剩余宝石价值中的最小值你会将该宝石放到所有宝石的最后
返回把宝石都送人需要多少天
比如arr = [3,1,4,3,1,2]
在第1天你遇到了价值3的宝石但是3并不是所有剩余宝石的价值最小值
所以你把3放在了所有宝石的最后arr = [1,4,3,1,2,3]
在第2天你遇到了价值1的宝石1是所有剩余宝石的价值最小值
所以你把价值1的宝石送人arr = [4,3,1,2,3]
在第3天你把价值4的宝石放到最后arr = [3,1,2,3,4]
在第4天你把价值3的宝石放到最后arr = [1,2,3,4,3]
在第5天你送出了价值1的宝石arr = [2,3,4,3]
在第6天你送出了价值2的宝石arr = [3,4,3]
在第7天你送出了价值3的宝石arr = [4,3]
在第8天你把价值4的宝石放到最后arr = [3,4]
在第9天你送出了价值3的宝石arr = [4]
在第10天你送出了价值4的宝石宝石已经没有了
所以返回10
1 <= N <= 10的5次方
1 <= 宝石价值 <= 10的9次方

Loading…
Cancel
Save