diff --git a/MCA算法突击课/第03期/mca_02/Code01_CoverMax.java b/MCA算法突击课/第03期/mca_02/Code01_CoverMax.java new file mode 100644 index 0000000..892a4ed --- /dev/null +++ b/MCA算法突击课/第03期/mca_02/Code01_CoverMax.java @@ -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 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; + } + +} diff --git a/MCA算法突击课/第03期/mca_02/Code02_LessMoneySplitGold.java b/MCA算法突击课/第03期/mca_02/Code02_LessMoneySplitGold.java new file mode 100644 index 0000000..9f0ea72 --- /dev/null +++ b/MCA算法突击课/第03期/mca_02/Code02_LessMoneySplitGold.java @@ -0,0 +1,22 @@ +package 第03期.mca_02; + +import java.util.PriorityQueue; + +public class Code02_LessMoneySplitGold { + + public static int lessMoney(int[] arr) { + PriorityQueue 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; + } + +} diff --git a/MCA算法突击课/第03期/mca_02/Code03_MinimumCostToHireKWorkers.java b/MCA算法突击课/第03期/mca_02/Code03_MinimumCostToHireKWorkers.java new file mode 100644 index 0000000..5fc6e73 --- /dev/null +++ b/MCA算法突击课/第03期/mca_02/Code03_MinimumCostToHireKWorkers.java @@ -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 minTops = new PriorityQueue((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; + } + +} diff --git a/MCA算法突击课/第03期/mca_02/Code03_SmallerEqualBigger.java b/MCA算法突击课/第03期/mca_02/Code03_SmallerEqualBigger.java deleted file mode 100644 index 586781f..0000000 --- a/MCA算法突击课/第03期/mca_02/Code03_SmallerEqualBigger.java +++ /dev/null @@ -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); - - } - -} diff --git a/MCA算法突击课/第03期/mca_02/Code07_FindFirstIntersectNode.java b/MCA算法突击课/第03期/mca_02/Code04_FindFirstIntersectNode.java similarity index 98% rename from MCA算法突击课/第03期/mca_02/Code07_FindFirstIntersectNode.java rename to MCA算法突击课/第03期/mca_02/Code04_FindFirstIntersectNode.java index 0fdd8a9..bf27963 100644 --- a/MCA算法突击课/第03期/mca_02/Code07_FindFirstIntersectNode.java +++ b/MCA算法突击课/第03期/mca_02/Code04_FindFirstIntersectNode.java @@ -1,6 +1,6 @@ package 第03期.mca_02; -public class Code07_FindFirstIntersectNode { +public class Code04_FindFirstIntersectNode { public static class Node { public int value; diff --git a/MCA算法突击课/第03期/mca_02/Code01_AddTwoNumbers.java b/MCA算法突击课/第03期/mca_02/Code05_AddTwoNumbers.java similarity index 97% rename from MCA算法突击课/第03期/mca_02/Code01_AddTwoNumbers.java rename to MCA算法突击课/第03期/mca_02/Code05_AddTwoNumbers.java index 12998b6..f76e576 100644 --- a/MCA算法突击课/第03期/mca_02/Code01_AddTwoNumbers.java +++ b/MCA算法突击课/第03期/mca_02/Code05_AddTwoNumbers.java @@ -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 { diff --git a/MCA算法突击课/第03期/mca_02/Code02_IsPalindromeList.java b/MCA算法突击课/第03期/mca_02/Code06_IsPalindromeList.java similarity index 99% rename from MCA算法突击课/第03期/mca_02/Code02_IsPalindromeList.java rename to MCA算法突击课/第03期/mca_02/Code06_IsPalindromeList.java index 815c245..9d2f1c7 100644 --- a/MCA算法突击课/第03期/mca_02/Code02_IsPalindromeList.java +++ b/MCA算法突击课/第03期/mca_02/Code06_IsPalindromeList.java @@ -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; diff --git a/MCA算法突击课/第03期/mca_02/Code04_CopyListWithRandom.java b/MCA算法突击课/第03期/mca_02/Code07_CopyListWithRandom.java similarity index 97% rename from MCA算法突击课/第03期/mca_02/Code04_CopyListWithRandom.java rename to MCA算法突击课/第03期/mca_02/Code07_CopyListWithRandom.java index ccf8664..d223ee5 100644 --- a/MCA算法突击课/第03期/mca_02/Code04_CopyListWithRandom.java +++ b/MCA算法突击课/第03期/mca_02/Code07_CopyListWithRandom.java @@ -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; diff --git a/MCA算法突击课/第03期/mca_02/Code08_CoverMax.java b/MCA算法突击课/第03期/mca_02/Code08_CoverMax.java deleted file mode 100644 index be5de70..0000000 --- a/MCA算法突击课/第03期/mca_02/Code08_CoverMax.java +++ /dev/null @@ -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 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 { - - @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 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 { - - @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 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"); - } - -} diff --git a/MCA算法突击课/第03期/mca_02/Code05_ReverseNodesInKGroup.java b/MCA算法突击课/第03期/mca_02/Code08_ReverseNodesInKGroup.java similarity index 96% rename from MCA算法突击课/第03期/mca_02/Code05_ReverseNodesInKGroup.java rename to MCA算法突击课/第03期/mca_02/Code08_ReverseNodesInKGroup.java index f27209d..9b59506 100644 --- a/MCA算法突击课/第03期/mca_02/Code05_ReverseNodesInKGroup.java +++ b/MCA算法突击课/第03期/mca_02/Code08_ReverseNodesInKGroup.java @@ -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 { diff --git a/MCA算法突击课/第03期/mca_02/Code09_LessMoneySplitGold.java b/MCA算法突击课/第03期/mca_02/Code09_LessMoneySplitGold.java deleted file mode 100644 index aa9eecd..0000000 --- a/MCA算法突击课/第03期/mca_02/Code09_LessMoneySplitGold.java +++ /dev/null @@ -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 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!"); - } - -} diff --git a/MCA算法突击课/第03期/mca_02/Code06_MergeKSortedLists.java b/MCA算法突击课/第03期/mca_02/Code09_MergeKSortedLists.java similarity index 96% rename from MCA算法突击课/第03期/mca_02/Code06_MergeKSortedLists.java rename to MCA算法突击课/第03期/mca_02/Code09_MergeKSortedLists.java index 14662fd..8c422b2 100644 --- a/MCA算法突击课/第03期/mca_02/Code06_MergeKSortedLists.java +++ b/MCA算法突击课/第03期/mca_02/Code09_MergeKSortedLists.java @@ -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; diff --git a/算法周更班/class_2023_02_3_week/Code01_SuperPalindromes.java b/算法周更班/class_2023_02_3_week/Code01_SuperPalindromes.java new file mode 100644 index 0000000..42f4135 --- /dev/null +++ b/算法周更班/class_2023_02_3_week/Code01_SuperPalindromes.java @@ -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; + } + +} diff --git a/算法周更班/class_2023_02_3_week/Code02_RecoverTreeFromPreorderTraversal.java b/算法周更班/class_2023_02_3_week/Code02_RecoverTreeFromPreorderTraversal.java new file mode 100644 index 0000000..5181a3f --- /dev/null +++ b/算法周更班/class_2023_02_3_week/Code02_RecoverTreeFromPreorderTraversal.java @@ -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; + } + +} diff --git a/算法周更班/class_2023_02_3_week/Code03_LongestWellPerformingInterval.java b/算法周更班/class_2023_02_3_week/Code03_LongestWellPerformingInterval.java new file mode 100644 index 0000000..fbfc65c --- /dev/null +++ b/算法周更班/class_2023_02_3_week/Code03_LongestWellPerformingInterval.java @@ -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 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; + } + +} diff --git a/算法周更班/class_2023_02_3_week/Code04_OperateScores.java b/算法周更班/class_2023_02_3_week/Code04_OperateScores.java new file mode 100644 index 0000000..27dff54 --- /dev/null +++ b/算法周更班/class_2023_02_3_week/Code04_OperateScores.java @@ -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, 表示将目前分数 桶 + TreeMap 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 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("性能测试结束"); + } + +} diff --git a/算法周更班/class_2023_02_3_week/Code05_SendOutDiamondDays.java b/算法周更班/class_2023_02_3_week/Code05_SendOutDiamondDays.java new file mode 100644 index 0000000..c1de560 --- /dev/null +++ b/算法周更班/class_2023_02_3_week/Code05_SendOutDiamondDays.java @@ -0,0 +1,256 @@ +package class_2023_02_3_week; + +import java.util.ArrayList; +import java.util.Arrays; + +// 来自TikTok美国笔试 +// 给定一个长度为N的数组arr,arr[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 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 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("性能测试结束"); + } + +} diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index c63d9c8..60fc22a 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -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, 表示将目前分数