地址:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
难度:中等
题目描述:给你一个下标从 0 开始长度为 n 的数组 nums 。
每一秒,你可以对数组执行以下操作:
- 对于范围在 [0, n - 1] 内的每一个下标 i ,将 nums[i] 替换成 nums[i] ,nums[(i - 1 + n) % n] 或者 nums[(i + 1) % n] 三者之一。
其实就是nums[i]可以替换成它本身或者它的前一位或者后一位的值,这个时候在想边缘怎么办?
把数组看出一个环,num[0]挨着的就是num[8]和num[1]
可以带入计算一下:n = 9 ,i = 0,
(i - 1 + n) % n = (0-1+9)%9 = 8
(i +1 + n) % n = (0+1+9)%9 = 10%9 = 1
注意,所有元素会被同时替换。
也就是一轮替换就是1s
比如可以同时操作num[1]、num[2])替换,等这一轮停止操作了我们得到了一个替换后的数组
入果这个数组没有符合预期,那就需要再进行到下一轮替换
请你返回将数组 nums 中所有元素变成相等元素所需要的 最少 秒数。
思考过程
可以理解为将数组的每一个元素都逐渐替换为数组中的某个值。
我们可以枚举数组中的各种元素,分别计算将所有元素替换为该元素所消耗的时间。
计算过程需要利用该元素在数组中出现的位置
题解
- 我们首先用哈希表,统计 nums 中相同的数所出现的位置,mp[x]表示 x 所出现的位置。
- 然后我们研究,使得数组全部变为 x所需要的时间,这个时间取决于 nums中,相邻 x 的最大距离。
- 我们依次枚举所有相邻(包括头尾)x 的索引值,找到最大的距离。
- 最大距离除以二并向上取整,就是使得数组全部变为 x 所需要的时间
- 最后我们对所有 nums中 的数,都找到所需的时间,返回其中的最小值即可。
本质上是扩散的原理
假设现在数组的为 3 2 6 4 9 5 3 1,x是3
分析:
我们能看到两个3之间最大的间隔有5个元素,那需要经过多少轮才能把3之间的元素同步扩散完呢?
第一次扩散(暂时不要关注数字1):
第二次扩散(暂时不要关注数字1):
第三次扩散(暂时不要关注数字1):
可以看到是经过了三次才完成了扩散,正好是5/2在向上取整的结果
为什么是除2?
因为每次两边都能同时扩散推进一个,效率是乘2的,自然最后计算的时候也就是除2啦
那短距离的那边怎么统计呢?
题目说了:所有元素会被同时替换。
也就是说,元素的扩散是同时进行的,长的距离扩散的时候,短的距离也在同步扩散,那个1在第一轮扩散的时候就被替换成3了
时间长的都扩散完了,时间短的其实早就扩散完毕了
代码解析:
function minimumSeconds(nums: number[]): number {
const n:number = nums.length
let rs:number = n
const mp:Map<number,number[]> = new Map() //存储结果
for(let i:number = 0;i<n;i++){
if (!mp.has(nums[i])) {
mp.set(nums[i], []);
}
mp.get(nums[i]).push(i);
}
for(const pos of mp.values()){
let mx: number = pos[0] + n - pos[pos.length - 1]-1;//为什么设置这个初始值?
// 避免环形的时候最大距离计算错误
for (let i: number = 1; i < pos.length; ++i) {
mx = Math.max(mx, pos[i] - pos[i - 1]-1);
}
rs = Math.min(rs, Math.ceil(mx / 2));
}
return rs
};
解释一下这行代码:number = pos[0] + n - pos[pos.length - 1]-1
我们知道我们需要找到的是最大距离,记住数组是环形数组
那么下面的情况,最大长度就会是pos[0] + n - pos[pos.length - 1]-1
如果我们还是拿2-0-1的话,计算出来的是短的距离,结果是1,会导致最终的答案不对