330. 按要求补齐数组(😦😦😦😦😦😦😦😦)
给定一个已排序的正整数数组 nums,和一个正整数 n 。从 [1, n] 区间内选取任意个数字补充到 nums 中,使得 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。
思路:贪心算法
找到当前数据(原有的+新加入的)所能覆盖的最大连续范围
一个数组[a1,a2,a3…an]当前能用和表示的数字区间为[1,right],此时往数组内补充新数num,则此时能表示的区间为[1,right]∪[num,right + num]
首先由于num被添加进了数组,则能实现的最大的数显然变成了right + num,而由于right之前的数[1, right]都可以实现,那么,实现[0+num,…,j+num,…right + num]就可以用 j+ num实现
本题的贪心思想即来源于此,为了使补充的新数物尽其用,能够直接扩大可表示的区间范围,把补充的num设为right + 1即可。此时能表示的数字区间可以直接更新为[1, ach + ach + 1],不会漏掉中间的数字。
所以本题的思路是这样的:
1.当前能表示的最大数字为right,则下一个需要达到的目标数字是right + 1,而当前(未使用)的数组元素为num = nums[index]
判断num与目标值right + 1的大小关系,
2.如果num > right + 1,则说明[right + 1, num - 1]区间内的数字无法表示,必须补充插入新数。为了使插入的新数既能表示right + 1,又能尽可能覆盖更多的数组(贪心的关键之处),插入的数字就是right + 1,更新right = right + right + 1
3.如果num < right + 1,说明当前的目标值right + 1可以通过num及其之前的数实现(因为num >= 1),此时更新right = right + num【即不需要额外添加元素就可以扩大连续最大覆盖范围】
class Solution {
public int minPatches(int[] nums, int n) {
//贪心算法:找到当前可覆盖的最大连续区间,用right表示当前能覆盖的连续范围的最大值
//使用int对于测试集可能会溢出
long right=0;//初始:能覆盖的最大连续范围为0
int count=0;//记录需要添加的元素的个数
int index=0;//记录nums的下标
while (right<n) {
//所覆盖的范围一定比i先到达n
if (index>=nums.length || right+1 < nums[index]) {
//当前能表示的最大数字为right,则下一个需要达到的目标数字是right + 1,而当前(未使用)的数组元素为num = nums[idx]
//如果right与nums[index]之间差的比1多,则需要插入新的元素
count++;
right+=right+1;
}
else {
//不需要添加元素就可以扩大覆盖范围
right += nums[index];
index++;
}
}
return count;
}
}