按要求补齐数组
一、题目简介
给定一个已排序的正整数数组 nums,和一个正整数 n 。从 [1, n] 区间内选取任意个数字补充到 nums 中,使得 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。
(题目来源:力扣(LeetCode))
示例 1:
输入: nums = [1,3], n = 6
输出: 1
解释:
根据 nums 里现有的组合 [1], [3], [1,3],可以得出 1, 3, 4。
现在如果我们将 2 添加到 nums 中, 组合变为: [1], [2], [3], [1,3], [2,3], [1,2,3]。
其和可以表示数字 1, 2, 3, 4, 5, 6,能够覆盖 [1, 6] 区间里所有的数。
所以我们最少需要添加一个数字。
示例 2:
输入: nums = [1,5,10], n = 20
输出: 2
解释: 我们需要添加 [2, 4]。
示例 3:
输入: nums = [1,2,2], n = 5
输出: 0
二、解决方法
1. 求覆盖范围
package com.lxf.test;
public class MinPatches {
/**
* 我们可以直接用范围来统计数字:
* 我们遍历n和数组中的值,每次选择加入的数字是数组中的值还是i(遍历n的当前值):
* 1.数组中的值用完(没办法只能取i的值)或i<数组当前值(数组当前值<=n,说明当前还没覆盖到n,加入i是没问题的)
* 此时覆盖范围:cover=cover(上一次的覆盖范围)+i;此时加入的数字数量count要加1
*
* 2.上面两种情况外(数组值有且i>=数组当前值),这时肯定要取数组中的值(数组的数肯定要计算覆盖范围,我们又不能丢了它)
* 此时覆盖范围:cover=cover(上一次的覆盖范围)+i;此时加入的数字是数组中原有的count不需要变
*
* 注意:每一次循环都要从cover+1开始,因为覆盖范围内的数字没必要考虑了,此时数组中数都能计算覆盖范围内的数字
* 3.比如:nums=[2,4,9] n=10
* 开始遍历n(1,2,3,4,5,6,7,8,9,10)
* ①当前是2, 数组当前值是2大于它.覆盖范围=1,当前加入的数字=1,下一波i=2
* ②当前是2, 数组当前值是2等于它.覆盖范围=3,当前加入的数字=1(不变),下一波i=4
* ③当前是4,数组当前值是4等于它.覆盖范围=7,当前加入的数字=1(不变),下一波i=8
* ④当前是8,数组当前值是9大于它.覆盖范围=15,当前加入的数字=2.下一波i=16>n结束遍历
* 答案=2
* @param nums
* @param
* @return
*/
public int minPatches(int[] nums, int n) {
//覆盖范围
long cover=0;
//数组大小
int m=nums.length;
//加入的数字数目
int count=0;
//数组下标
int index=0;
for (long i =1;i<=n; ) {
//如果当前数组的值都计算到覆盖范围或者当前要加入的数字比当前数组中的值要小
//加上i的值同时加入的数字数目加1
if(index>=m||i<nums[index]){
cover+=i;
count++;
}else{
//否则就加入数组中的值,再指向下一个数字
cover+=nums[index];
index++;
}
//i每次从覆盖范围+1开始
i=cover+1;
}
return count;
}
}
2.贪心算法(官方题解)
class Solution {
public int minPatches(int[] nums, int n) {
//加入的数字数
int patches = 0;
//覆盖范围
long x = 1;
//数组长度及下标
int length = nums.length, index = 0;
//一直遍历,直到覆盖范围大于或等于n
while (x <= n) {
//如果此时数组值还没加入覆盖范围,并且小于等于覆盖范围
//则加入数组范围,且不算新加入(原本就是数组中的值)
if (index < length && nums[index] <= x) {
x += nums[index];
index++;
} else {
//否则覆盖范围直接翻倍
//(当前覆盖范围x+上一次的覆盖范围x-1+1)=2x
x *= 2;
//加入的数字数+1
patches++;
}
}
return patches;
}
}