modify code

master
algorithmzuo 2 years ago
parent e0c47a27e8
commit afee1bccc9

@ -0,0 +1,233 @@
package class_2022_11_5_week;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
// 来自学员问题
// 给定一个数组componets长度为A
// componets[i] = j代表i类型的任务需要耗时j
// 给定一个二维数组orders长度为M
// orders[i][0]代表i号订单下单时间
// orders[i][1]代表i号订单是哪种类型的任务毫无疑问orders[i][1] < A
// 一开始所有流水线都在0时刻待命
// 给定一个正数nums表示流水线的数量流水线编号为0 ~ nums-1
// 每一个流水线可以承接任何类型的任务耗时就是componets数组给定的
// 所有订单的下单时间一定是有序的也就是orders数组是根据下单时间排序的
// 每一个订单开始执行的时间不能早于下单时间,
// 如果有多个流水线都可以执行当前订单,选择编号最小的流水线
// 根据上面说的任务执行细节,去依次完成所有订单
// 返回长度为M的数组ans也就是和orders等长
// ans[i][0]代表i号订单是由哪条流水线执行的
// ans[i][1]代表i号订单的完成时间
// 1 <= A <= 10^5
// 1 <= M <= 10^5
// 1 <= nums <= 10^5
// 1 <= 时间数值 <= 10^5
public class Code01_FinishOrdersEndTimes {
// jobTimes : [ 5 2 7 1 ]
// 0 1 2 3
// orders : [100, 0] [200, 1]
// 0 1
// nums = 7
// 0 1 2 ... 6
// 暴力方法
// 为了测试
public static int[][] times1(int nums, int[] jobTimes, int[][] orders) {
int[] lines = new int[nums];
int n = orders.length;
int[][] ans = new int[n][2];
for (int i = 0; i < n; i++) {
int start = orders[i][0];
int type = orders[i][1];
int usei = -1;
for (int j = 0; j < nums; j++) {
if (lines[j] <= start) {
usei = j;
break;
}
}
if (usei != -1) {
ans[i][0] = usei;
ans[i][1] = start + jobTimes[type];
} else {
int early = Integer.MAX_VALUE;
for (int j = 0; j < nums; j++) {
if (lines[j] < early) {
early = lines[j];
usei = j;
}
}
ans[i][0] = usei;
ans[i][1] = early + jobTimes[type];
}
lines[usei] = ans[i][1];
}
return ans;
}
// 正式方法
// 时间复杂度O(M*logN)
// M是订单数量N是流水线数量
public static int[][] times2(int nums, int[] componets, int[][] orders) {
int n = orders.length;
// 睡眠堆
PriorityQueue<Line> sleepLines = new PriorityQueue<>(new WakeUpComparator());
// 可用堆
PriorityQueue<Line> canUseLines = new PriorityQueue<>(new IndexComparator());
for (int i = 0; i < nums; i++) {
canUseLines.add(new Line(0, i));
}
int[][] ans = new int[n][2];
for (int i = 0; i < orders.length; i++) {
int startTime = orders[i][0];
int jobType = orders[i][1];
// 当前订单在start时刻下单所有唤醒时间比time早的流水线全可以考虑
while (!sleepLines.isEmpty() && sleepLines.peek().time <= startTime) {
canUseLines.add(sleepLines.poll());
}
// 如果可以使用的流水线不存在
// 比如2条流水线
// 第0个订单1时刻开始用时100万流水线A在100万+1时刻醒来
// 第1个订单2时刻开始用时100万流水线B在100万+2时刻醒来
// 轮到第3个订单3时刻开始用时100万
// 会发现可用流水线已经没有了此时需要等到流水线A在100万+1时刻醒来做当前订单
Line use = null;
if (canUseLines.isEmpty()) {
// 如果当前时刻,可以使用的流水线不存在,需要等到可以唤醒的最早那个
// 如果可以唤醒的最早流水线,不只一个
// 选编号小的,看比较器的注释
use = sleepLines.poll();
ans[i][1] = use.time + componets[jobType];
} else {
// 如果当前时刻,可以使用的流水线存在,需要使用编号最小的
use = canUseLines.poll();
ans[i][1] = startTime + componets[jobType];
}
ans[i][0] = use.index;
use.time = ans[i][1];
sleepLines.add(use);
}
return ans;
}
// 流水线
public static class Line {
public int time;
public int index;
public Line(int t, int i) {
time = t;
index = i;
}
}
public static class WakeUpComparator implements Comparator<Line> {
// 谁能早醒谁在前
// 醒来时间一样,编号小的在前
// 为什么要考虑编号,看上面的注释
// 如果可以使用的流水线不存在
// 那要使用最早唤醒的那个流水线来执行当前订单
// 但是如果最早唤醒的那个流水线不只一个,还是要选编号小的
@Override
public int compare(Line o1, Line o2) {
if (o1.time != o2.time) {
return o1.time < o2.time ? -1 : 1;
}
return o1.index - o2.index;
}
}
public static class IndexComparator implements Comparator<Line> {
@Override
public int compare(Line o1, Line o2) {
return o1.index - o2.index;
}
}
// 为了测试
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) + 1;
}
return ans;
}
// 为了测试
public static int[][] randomMatrix(int n, int a, int b) {
int[][] ans = new int[n][2];
for (int i = 0; i < n; i++) {
ans[i][0] = (int) (Math.random() * a) + 1;
ans[i][1] = (int) (Math.random() * b);
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int N = 100;
int M = 300;
int V = 10000;
int testTimes = 5000;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int nums = (int) (Math.random() * N) + 1;
int orderNumber = (int) (Math.random() * M) + 1;
int types = (int) (Math.random() * N) + 1;
int[] componets = randomArray(types, V);
int[][] orders = randomMatrix(orderNumber, V, types);
Arrays.sort(orders, (a, b) -> a[0] - b[0]);
int[][] ans1 = times1(nums, componets, orders);
int[][] ans2 = times2(nums, componets, orders);
for (int j = 0; j < ans1.length; j++) {
if (ans1[j][0] != ans2[j][0] || ans1[j][1] != ans2[j][1]) {
System.out.println("出错了!");
System.out.println(nums);
for (int num : componets) {
System.out.print(num + " ");
}
System.out.println();
for (int[] order : orders) {
System.out.print("(" + order[0] + "," + order[1] + ") ");
}
System.out.println();
System.out.print("ans1 : ");
for (int[] cur : ans1) {
System.out.print("(" + cur[0] + "," + cur[1] + ") ");
}
System.out.println();
System.out.print("ans2 : ");
for (int[] cur : ans2) {
System.out.print("(" + cur[0] + "," + cur[1] + ") ");
}
System.out.println();
return;
}
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
N = 100000;
M = 500000;
V = 100000;
int nums = N;
int[] componets = randomArray(N, V);
int[][] orders = randomMatrix(M, V, N);
Arrays.sort(orders, (a, b) -> a[0] - b[0]);
System.out.println("流水线数量 : " + N);
System.out.println("订单数量 : " + M);
System.out.println("时间数值范围 : " + V);
long start = System.currentTimeMillis();
times2(nums, componets, orders);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,35 @@
package class_2022_11_5_week;
// 将一个给定字符串 s 根据给定的行数 numRows
// 以从上往下、从左到右进行 Z 字形排列
// 比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下
// P A H N
// A P L S I I G
// Y I R
// 之后,你的输出需要从左往右逐行读取,产生出一个新的字符串
// "PAHNAPLSIIGYIR"
// 请你实现这个将字符串进行指定行数变换的函数
// string convert(string s, int numRows)
// 测试链接 : https://leetcode.cn/problems/zigzag-conversion/
public class Code02_ZigZagConversion {
public static String convert(String s, int row) {
int n = s.length();
if (row == 1 || row >= n) {
return s;
}
int t = 2 * (row - 1);
char[] ans = new char[n];
int fill = 0;
for (int i = 0; i < row; i++) {
for (int j = i, nextColTop = t; j < n; j += t, nextColTop += t) {
ans[fill++] = s.charAt(j);
if (i >= 1 && i <= row - 2 && nextColTop - i < n) {
ans[fill++] = s.charAt(nextColTop - i);
}
}
}
return String.valueOf(ans);
}
}

@ -0,0 +1,32 @@
package class_2022_11_5_week;
import java.util.Arrays;
// 一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。
// 给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和
// 由于答案可能非常大,请返回对 109 + 7 取余 后的结果。
// 子序列 定义为从一个数组里删除一些(或者不删除)元素,
// 但不改变剩下元素的顺序得到的数组
// 例如,[3,6,2,7] 就是数组 [0,3,1,6,2,2,7] 的一个子序列。
// 测试链接 : https://leetcode.cn/problems/sum-of-subsequence-widths/
public class Code03_SumOfSubSequenceWidths {
public static int sumSubseqWidths(int[] nums) {
Arrays.sort(nums);
int mod = 1000000007;
long ans = 0;
long A = 0;
long B = 0;
long C = 1;
long D = C;
for (int i = 1; i < nums.length; i++) {
A = (D * nums[i]) % mod;
B = (B * 2 + nums[i - 1]) % mod;
ans = (ans + A - B + mod) % mod;
C = (C * 2) % mod;
D = (D + C) % mod;
}
return (int) (ans);
}
}

@ -0,0 +1,92 @@
package class_2022_11_5_week;
// 给你一个整数 n
// 请你在无限的整数序列 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...]
// 中找出并返回第 n 位上的数字。
// 测试链接 : https://leetcode.cn/problems/nth-digit/
// 1 <= n <= 2^31 - 1
public class Code04_NthDigit {
public static final long[] under = {
0L,// 0位数一共能解决几个位
9L,// 1位数一共能解决几个位
189L,// 1~2位数一共能解决几个位
2889L,// 1~3位数一共能解决几个位
38889L,
488889L,
5888889L,
68888889L,
788888889L,
8888888889L,
98888888889L };
public static final int[] help = {
0,
1, // 1
10,// 2
100,// 3
1000, // 4
10000,
100000,
1000000,
10000000,
100000000,
1000000000 };
public static int findNthDigit(int n) {
int len = 0;
for (int i = 1; i < under.length; i++) {
if (under[i] >= n) {
len = i;
break;
}
}
// 算出几位够用!
// 5位数够用
// nth - 1~4位所有的整数帮忙搞定的数字个数
return number(0, len, help[len], help[len], (int) (n - under[len - 1]));
}
// path : 路径 左(低) <- 右(高)
// len : n -> 5位数 len = 5 固定!
// offset : 10000 目前要决定的是高1位
// 1000 目前要决定的是高2位
// 10 目前要决定的是高2位
// 可变
// all : 10000 固定
// nth : 第几个
public static int number(int path, int len, int offset, int all, int nth) {
if (offset == 0) {
// 5 3 2 1 6
// path : 6 1 2 3 5
// 5 4 3 2 1
return (path / help[nth]) % 10;
} else {
// 3 _ _ _ _
int cur = 0;
int minus = 0;
// i是开始尝试的数字 最高位 i 1 2 3 4...
// 不是最高位 0 1 2 3 4
// 股数j = 1 2 3 4
for (int i = offset == all ? 1 : 0, j = 1; i <= 9; i++, j++) {
// 搞定了多少
long under = (long) j * len * offset;
if (under >= nth) {
cur = i;
break;
}
// < i不要
minus = (int) under;
}
// 1458 - 1200
// cur !
// n - minus
// 10000
// !1000
// !!100
// !!!10
return number(cur * (all / offset) + path, len, offset / 10, all, nth - minus);
}
}
}

@ -2530,6 +2530,54 @@ b = { 100, 70, 20, 40, 150 }
第050节 2022年11月第5周流行算法题目解析
来自学员问题
给定一个数组componets长度为A
componets[i] = j代表i类型的任务需要耗时j
给定一个二维数组orders长度为M
orders[i][0]代表i号订单下单时间
orders[i][1]代表i号订单是哪种类型的任务毫无疑问orders[i][1] < A
一开始所有流水线都在0时刻待命
给定一个正数nums表示流水线的数量流水线编号为0 ~ nums-1
每一个流水线可以承接任何类型的任务耗时就是componets数组给定的
所有订单的下单时间一定是有序的也就是orders数组是根据下单时间排序的
每一个订单开始执行的时间不能早于下单时间,
如果有多个流水线都可以执行当前订单,选择编号最小的流水线
根据上面说的任务执行细节,去依次完成所有订单
返回长度为M的数组ans也就是和orders等长
ans[i][0]代表i号订单是由哪条流水线执行的
ans[i][1]代表i号订单的完成时间
1 <= A <= 10^5
1 <= M <= 10^5
1 <= nums <= 10^5
1 <= 时间数值 <= 10^5
将一个给定字符串 s 根据给定的行数 numRows
以从上往下、从左到右进行 Z 字形排列
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串
"PAHNAPLSIIGYIR"
请你实现这个将字符串进行指定行数变换的函数
string convert(string s, int numRows)
测试链接 : https://leetcode.cn/problems/zigzag-conversion/
一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。
给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和
由于答案可能非常大,请返回对 109 + 7 取余 后的结果。
子序列 定义为从一个数组里删除一些(或者不删除)元素,
但不改变剩下元素的顺序得到的数组
例如,[3,6,2,7] 就是数组 [0,3,1,6,2,2,7] 的一个子序列。
测试链接 : https://leetcode.cn/problems/sum-of-subsequence-widths/
给你一个整数 n
请你在无限的整数序列 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...]
中找出并返回第 n 位上的数字。
测试链接 : https://leetcode.cn/problems/nth-digit/
1 <= n <= 2^31 - 1

Loading…
Cancel
Save