2023-06-20:给定一个长度为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次方。

来自TikTok美国笔试。

答案2023-06-20:

1.第一个方法(days1)使用了暴力的方式,通过遍历数组并移动宝石来模拟每一天的操作,直到所有宝石都被送出。时间复杂度较高。

2.第二个方法(days2)使用了更高效的算法。首先构建了一个支持查询累加和和最小值的数据结构(IndexTree和SegmentTree)。然后利用这些数据结构来计算送出所有宝石需要的天数。具体步骤如下:

2.1.初始化累加和数据结构(it)和最小值数据结构(st)。

2.2.设定起始位置(start)为1,找到剩余宝石中的最小值(find)。

2.3.计算从起始位置到最小值之间的宝石总数(daysCount)。

2.4.将最小值送出,更新累加和数据结构(it)和最小值数据结构(st)。

2.5.更新起始位置(start)为最小值。

2.6.重复上述步骤直到所有宝石都被送出。

2.7.返回送出宝石所需的天数。

时间复杂度和空间复杂度如下:

方法1(days1):

  • 时间复杂度:$O(N^2)$,其中N是宝石数组的长度。需要遍历数组N次,并且在每次操作中需要移动宝石,移动的次数也达到了N次。
  • 空间复杂度:O(N),需要额外的存储空间来存储宝石数组。

方法2(days2):

  • 时间复杂度:$O(N * (logN)^2)$,其中N是宝石数组的长度。构建IndexTree和SegmentTree所需的时间复杂度为O(N * logN)。每次查询最小值的时间复杂度为O(logN),总共进行N次查询。因此,总的时间复杂度为$O(N * (logN)^2)$。
  • 空间复杂度:O(N),需要额外的存储空间来构建IndexTree和SegmentTree。

综上所述,方法1的时间复杂度为$O(N^2)$,方法2的时间复杂度为$O(N * (logN)^2)$。在时间复杂度上,方法2优于方法1。方法1的空间复杂度为O(N),方法2的空间复杂度为O(N)。在空间复杂度上,两种方法相同。

go完整代码如下:

package main

import (
"fmt"
"math"
"math/rand"
"time"
) // 暴力方法
// 为了验证
func days1(diamonds []int) int {
arr := make([]int, len(diamonds))
copy(arr, diamonds)
ans := 0
for len(arr) > 0 {
ans++
deal(&arr)
}
return ans
} // 暴力方法
// 为了验证
func deal(arr *[]int) {
head := (*arr)[0]
*arr = (*arr)[1:]
min := head
for _, num := range *arr {
min = int(math.Min(float64(min), float64(num)))
}
if head > min {
*arr = append(*arr, head)
}
} // 正式方法
// 时间复杂度O(N * (logN)的平方)
func days2(diamonds []int) int {
// n : 位置
n := len(diamonds)
// 1 ~ n : 1
it := NewIndexTree(n)
// 7 6 2...
// 1 2 3....
st := NewSegmentTree(diamonds)
days := 0
find, start := 1, 1
for it.SumRange(1, n) != 0 {
// start ..... find(后续....最小值,最左的位置)
find = findMin(st, start, n)
days += daysCount(it, start, find, n)
// 1
// find
it.Add(find, -1)
st.Update(find, math.MaxInt32)
start = find
}
return days
} func findMin(st *SegmentTree, start, n int) int {
// start....n 左部分 1 ~ start-1 右
var l, r, min = n, 1, st.Min(1, n)
if st.Min(start, n) == min {
l = start
r = n
} else {
l = 1
r = start - 1
}
var m, ans = -1, -1
for l <= r {
m = (l + r) / 2
if st.Min(l, m) == min {
ans = m
r = m - 1
} else {
l = m + 1
}
}
return ans
} func daysCount(it *IndexTree, start, find, n int) int {
if start <= find {
return it.SumRange(start, find)
} else {
return it.SumRange(start, n) + it.SumRange(1, find)
}
} // 支持查询累加和
type IndexTree struct {
tree []int
n int
} func NewIndexTree(size int) *IndexTree {
it := &IndexTree{
tree: make([]int, size+1),
n: size,
}
for i := 1; i <= size; i++ {
it.Add(i, 1)
}
return it
} func (it *IndexTree) Sum(i int) int {
ret := 0
for i > 0 {
ret += it.tree[i]
i -= i & -i
}
return ret
} func (it *IndexTree) SumRange(l, r int) int {
return it.Sum(r) - it.Sum(l-1)
} func (it *IndexTree) Add(i, d int) {
for i <= it.n {
it.tree[i] += d
i += i & -i
}
} // 支持查询最小值
type SegmentTree struct {
n int
min []int
} func NewSegmentTree(arr []int) *SegmentTree {
n := len(arr)
st := &SegmentTree{
n: n,
min: make([]int, (n+1)<<2),
}
for i := 1; i <= n; i++ {
st.Update(i, arr[i-1])
}
return st
} func (st *SegmentTree) Update(i, v int) {
st.update(i, i, v, 1, st.n, 1)
} func (st *SegmentTree) update(L, R, C, l, r, rt int) {
if L <= l && r <= R {
st.min[rt] = C
return
}
mid := (l + r) >> 1
if L <= mid {
st.update(L, R, C, l, mid, rt<<1)
}
if R > mid {
st.update(L, R, C, mid+1, r, rt<<1|1)
}
st.pushUp(rt)
} func (st *SegmentTree) pushUp(rt int) {
st.min[rt] = int(math.Min(float64(st.min[rt<<1]), float64(st.min[rt<<1|1])))
} func (st *SegmentTree) Min(l, r int) int {
return st.minQuery(l, r, 1, st.n, 1)
} func (st *SegmentTree) minQuery(L, R, l, r, rt int) int {
if L <= l && r <= R {
return st.min[rt]
}
mid := (l + r) >> 1
ans := math.MaxInt32
if L <= mid {
ans = int(math.Min(float64(ans), float64(st.minQuery(L, R, l, mid, rt<<1))))
}
if R > mid {
ans = int(math.Min(float64(ans), float64(st.minQuery(L, R, mid+1, r, rt<<1|1))))
}
return ans
} // 为了测试
func randomArray(n, v int) []int {
arr := make([]int, n)
for i := 0; i < n; i++ {
arr[i] = rand.Intn(v)
}
return arr
} // 为了测试
func main() {
rand.Seed(time.Now().UnixMilli())
fmt.Println("例子测试开始")
arr := []int{3, 1, 4, 3, 1, 2}
fmt.Println(days1(arr))
fmt.Println(days2(arr))
fmt.Println("例子测试结束") N := 100
V := 100000
testTimes := 1000
fmt.Println("随机测试开始")
for i := 0; i < testTimes; i++ {
n := rand.Intn(N) + 1
diamonds := randomArray(n, V)
ans1 := days1(diamonds)
ans2 := days2(diamonds)
if ans1 != ans2 {
fmt.Println("出错了!")
}
}
fmt.Println("随机测试结束") fmt.Println("性能测试开始")
n := 100000
v := 1000000000
diamonds := randomArray(n, V)
fmt.Println("宝石数量 : ", n)
fmt.Println("价值范围 : ", v)
start := time.Now()
days2(diamonds)
end := time.Now()
fmt.Println("运行时间 : ", end.Sub(start).Milliseconds(), " 毫秒")
fmt.Println("性能测试结束")
}

rust完整代码如下:

use std::cmp;
use std::time::SystemTime; struct IndexTree {
tree: Vec<i64>,
n: i64,
} impl IndexTree {
fn new(size: i64) -> IndexTree {
let tree = vec![0; (size + 1) as usize];
let mut it = IndexTree {
tree: tree,
n: size,
};
for i in 1..=size {
it.add(i, 1);
}
it
} fn sum(&self, mut i: i64) -> i64 {
let mut ret = 0;
while i > 0 {
ret += self.tree[i as usize];
i -= i & -i;
}
ret
} fn sum_range(&self, l: i64, r: i64) -> i64 {
self.sum(r) - self.sum(l - 1)
} fn add(&mut self, mut i: i64, d: i64) {
while i <= self.n {
self.tree[i as usize] += d;
i += i & -i;
}
}
} struct SegmentTree {
n: i64,
min: Vec<i64>,
} impl SegmentTree {
fn new(arr: &[i64]) -> SegmentTree {
let n = arr.len() as i64;
let min = vec![0; ((n + 1) << 2) as usize];
let mut st = SegmentTree { n: n, min: min };
for i in 1..=n {
st.update(i, arr[(i - 1) as usize]);
}
st
} fn update(&mut self, i: i64, v: i64) {
self.update_segment(i, i, v, 1, self.n, 1);
} fn update_segment(&mut self, L: i64, R: i64, C: i64, l: i64, r: i64, rt: i64) {
if L <= l && r <= R {
self.min[rt as usize] = C;
return;
}
let mid = (l + r) >> 1;
if L <= mid {
self.update_segment(L, R, C, l, mid, rt << 1);
}
if R > mid {
self.update_segment(L, R, C, mid + 1, r, rt << 1 | 1);
}
self.push_up(rt);
} fn push_up(&mut self, rt: i64) {
self.min[rt as usize] = cmp::min(
self.min[(rt << 1) as usize],
self.min[(rt << 1 | 1) as usize],
);
} fn min_query(&self, L: i64, R: i64, l: i64, r: i64, rt: i64) -> i64 {
if L <= l && r <= R {
return self.min[rt as usize];
}
let mid = (l + r) >> 1;
let mut ans = i64::MAX;
if L <= mid {
ans = cmp::min(ans, self.min_query(L, R, l, mid, rt << 1));
}
if R > mid {
ans = cmp::min(ans, self.min_query(L, R, mid + 1, r, rt << 1 | 1));
}
ans
} fn min(&self, l: i64, r: i64) -> i64 {
self.min_query(l, r, 1, self.n, 1)
}
} fn days1(diamonds: &mut [i64]) -> i64 {
let mut arr = diamonds.to_vec();
let mut ans = 0;
while !arr.is_empty() {
ans += 1;
deal(&mut arr);
}
ans
} fn deal(arr: &mut Vec<i64>) {
let head = arr.remove(0);
let mut min0 = head;
for a in arr.iter() {
min0 = min0.min(*a);
}
if head > min0 {
arr.push(head);
}
} fn days2(diamonds: &[i64]) -> i64 {
let n = diamonds.len() as i64;
let mut it = IndexTree::new(n);
let mut st = SegmentTree::new(diamonds);
let mut days = 0;
let mut find = 1;
let mut start = 1;
while it.sum_range(1, n) != 0 {
find = find_min(&st, start, n);
days += days_count(&it, start, find, n);
it.add(find, -1);
st.update(find, i64::MAX);
start = find;
}
days
} fn find_min(st: &SegmentTree, start: i64, n: i64) -> i64 {
let (mut l, mut r, mut min) = (n, 1, st.min(1, n));
if st.min(start, n) == min {
l = start;
r = n;
} else {
l = 1;
r = start - 1;
}
let (mut m, mut ans) = (-1, -1);
while l <= r {
m = (l + r) >> 1;
if st.min(l, m) == min {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
ans
} fn days_count(it: &IndexTree, start: i64, find: i64, n: i64) -> i64 {
if start <= find {
it.sum_range(start, find)
} else {
it.sum_range(start, n) + it.sum_range(1, find)
}
} fn random_array(n: i64, v: i64) -> Vec<i64> {
let mut arr = vec![0; n as usize];
for i in 0..n {
arr[i as usize] = ((rand::random::<i64>() % v) + v) % v;
}
arr
} fn main() {
let now = SystemTime::now(); println!("例子测试开始");
let arr = vec![3, 1, 4, 3, 1, 2];
println!("{}", days1(&mut arr.to_vec()));
println!("{}", days2(&arr));
println!("例子测试结束"); let n = 100;
let v = 100000;
let test_times = 1000;
println!("随机测试开始");
for _ in 0..test_times {
let n = ((rand::random::<i64>() % n) + n) % n + 1;
let diamonds = random_array(n, v);
let ans1 = days1(&mut diamonds.clone());
let ans2 = days2(&diamonds);
if ans1 != ans2 {
println!("出错了!");
}
}
println!("随机测试结束"); println!("性能测试开始");
let n = 100000;
let v = 1000000000;
let diamonds = random_array(n, v);
println!("宝石数量 : {}", n);
println!("价值范围 : {}", v);
let start = SystemTime::now();
days2(&diamonds);
let end = SystemTime::now();
println!(
"运行时间 : {} 毫秒",
end.duration_since(start).unwrap().as_millis()
); println!("性能测试结束");
}

2023-06-20:给定一个长度为N的数组arr,arr[i]表示宝石的价值 你在某天遇到X价值的宝石, X价值如果是所有剩余宝石价值中的最小值,你会将该宝石送人 X价值如果不是所有剩余宝石价值中的的更多相关文章

  1. 面试题:给定一个长度为N的数组,其中每个元素的取值范围都是1到N。判断数组中是否有重复的数字

    题目:给定一个长度为N的数组,其中每个元素的取值范围都是1到N.判断数组中是否有重复的数字.(原数组不必保留) 方法1.对数组进行排序(快速,堆),然后比较相邻的元素是否相同.时间复杂度为O(nlog ...

  2. 给定一个长度为N的数组,找出出现次数大于n/2,n/3的数,要求时间复杂度O(n),空间复杂度O(1)

    先讨论出现次数大于n/2的数字,如果这样的数字存在,那么这个数出现的次数大于其他数出现的次数的总和. 在数组A中,我们定义两个数据集合a1,a2.a1为出现次数大于n/2的数的集合,a2为其余数组成的 ...

  3. java—数组乘积输入: 一个长度为n的整数数组input 输出: 一个长度为n的数组result,满足result[i] = input数组中,除了input[i] 之外的所有数的乘积,不用考虑溢出例如 input {2, 3, 4, 5} output: {60, 40, 30, 24}

    /** * 小米关于小米笔试题 数组乘积输入: 一个长度为n的整数数组input 输出: 一个长度为n的数组result,满足result[i] = * input数组中,除了input[i] 之外的 ...

  4. 给定数组a[1,2,3],用a里面的元素来生成一个长度为5的数组,打印出其排列组合

    给定数组a[1,2,3],用a里面的元素来生成一个长度为5的数组,打印出其排列组合 ruby代码: def all_possible_arr arr, length = 5 ret = [] leng ...

  5. 用最小的空间复杂度找出一个长度为n的数组且数据中的元素是[0,n-1]中任一个重复的数据。

    用最小的空间复杂度找出一个长度为n的数组且数据中的元素是[0,n-1]中任一个重复的数据. 比如:[1, 2, 3, 3, 2, 2, 6, 7, 8, 9] 中 2 or 3 分析:这道题目,实现比 ...

  6. 不用循环,、es6创建一个长度为100的数组

    问题描述:在不使用循环的条件下,如何创建一个长度为100的数组,并且数组的每一个元素是该元素的下标? 结果为: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1 ...

  7. 创建一个长度是5的数组,并填充随机数。使用for循环或者while循环,对这个数组实现反转效果

    package day01; import java.util.Random; /** * 首先创建一个长度是5的数组,并填充随机数.使用for循环或者while循环,对这个数组实现反转效果 * @a ...

  8. 前端面试题:不使用loop循环,创建一个长度为100的数组,并且每个元素的值等于它的下标,,怎么实现好?

    昨天,看这道题,脑子锈住了,就是没有思路,没看明白是什么意思?⊙﹏⊙|∣今天早上起床,想到需要思考一下这个问题. 当然,我没想明白为什么要这样做?(创建一个长度为100的数组,并且每个元素的值等于它的 ...

  9. 一个简单的算法,定义一个长度为n的数组,随机顺序存储1至n的的全部正整数,不重复。

    前些天看到.net笔试习题集上的一道小题,要求将1至100内的正整数随机填充到一个长度为100的数组,求一个简单的算法. 今天有空写了一下.代码如下,注释比较详细: using System; usi ...

  10. [LeetCode每日一题]153.寻找旋转排序数组中的最小值

    [LeetCode每日一题]153.寻找旋转排序数组中的最小值 问题 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组 nums = [0,1, ...

随机推荐

  1. C++的一些随笔(第一篇)

    C++中 ->的作用 ->用于指针 ->用于指向结构体的指针 ->用于指向结构体的指针,表示结构体内的元素  #include<stdio.h> struct ro ...

  2. 时隔十年,QQ更新了Linux版本

    昨天1024程序员节,QQ悄悄地更新了QQ for Linux,也许是给各位一个惊喜吧. 官网及其的简陋.和一个Word文档似的. 十年一更,有网友称,瞬间回到QQ2006,确实界面功能有些落后,相信 ...

  3. wpf CommunityToolkit.Mvvm8.1 MVVM工具包安装引用指南

    CommunityToolkit.Mvvm包(又名MVVM 工具包,以前名为 Microsoft.Toolkit.Mvvm)是一个现代.快速且模块化的 MVVM 库.它支持:.NET Standard ...

  4. shell读取配置文件-sed命令

    在编写启动脚本时,涉及到读取配置文件,特地记录下shell脚本读取启动文件的方式.主要提供两种格式的读取方式,方式一配置文件采用"[]"进行分区,方式二配置文件中需要有唯一的配置项 ...

  5. 3D开发工具HOOPS最新解析合集!助力实现web端高性能模型渲染!

    一.3D技术为创新提供强大助力(1)3D专家提供专属技术支持服务不管您想搭建桌面.WEB或者移动端APP应用,技术领先全球的HOOPS Platform组件都可以为您提供弹性的3D集成架构,同时,一批 ...

  6. 生成df的几种方法

    法一: pd.DataFrame( [ (第一行),(第二行),(第三行)] ) df = pd.DataFrame([('bird', 389.0), ('bird', 24.0), ('mamma ...

  7. php正则表达式大全/php正则表达式使用方法整理集合

    匹配数字 "^\d+$" //非负整数(正整数 + 0) "[1][1-9][0-9]$" //正整数 "^((-\d+)|(0+))$" ...

  8. 聊聊Redis sentinel 机制

    Redis 的哨兵机制自动完成了以下三大功能,从而实现了主从库的自动切换,可以降低 Redis 集群的运维开销: 监控主库运行状态,并判断主库是否客观下线: 在主库客观下线后,选取新主库: 选出新主库 ...

  9. 吃透SpringMVC面试八股文

    说说你对 SpringMVC 的理解 SpringMVC是一种基于 Java 的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于Spring框架的一个模块. 它通过一套注解,让一个简单的Jav ...

  10. c语言趣味编程(4)抓交通肇事犯

    一.问题描述 一辆卡车违反交通规则,撞人后逃跑.现场有三人目击该事件,但都没有记住车号,只记下车号的一些特征. 甲说:牌照的前两位数字是相同的: 乙说:牌照的后两位数字是相同的,但与前两位不同: 丙是 ...