You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
6.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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("性能测试结束");
}
}