1、题目描述
给你一个 严格升序排列 的正整数数组 arr 和一个整数 k 。
请你找到这个数组里第 k 个缺失的正整数。
示例1:
输入:arr = [2,3,4,7,11], k = 5
输出:9
解释:缺失的正整数包括 [1,5,6,8,9,10,12,13,...] 。第 5 个缺失的正整数为 9 。
示例2:
输入: arr = [1,2,3,4], k = 2
输出: 6
解释: 缺失的正整数包括 [5,6,7,...] 。第 2 个缺失的正整数为 6 。
2、思路
方法一:枚举
-
首先用一个变量current表示当前应该出现的数,从1开始,每次循环都让current递增
-
用指针ptr指向数组中没有匹配的第一个元素
-
每轮循环中将该元素和current进行比较,如果相等,则将指针后移,否则指针原地不动,代表current元素缺失。
-
利用missCount记录缺失的正整数个数,利用lastMiss记录最后一个缺失的正整数
-
需要注意的是,数组有效范围内缺失元素个数小于k的时候,需要继续往后遍历 方法二:二分查找
-
对于数组每个元素ai,;我们都可以知道到第i个元素为止缺失的元素数量为ai-i-1
-
观察每个元素对应的缺失个数,发现是一个非严格递增序列,我们可以在该序列中,通过二分查找,找到k所对应的位置。
-
根据这种分析,我们就可以在这个缺失数量的序列上进行二分查找,确定一个区间[i,j],满足lack[i] < k <= lack[j],则第k个缺失的数为k - (arr[i] - i - 1) + arr[i],arr[i] - i - 1表示arr[i]位置缺少的元素个数,k - 缺失个数表示从arr[i]开始还缺少几个元素,再加上arr[i],就是第k个缺失的元素。
-
在这里我们需要注意两个边界值的处理,一个是当a0大于k的时候,提前判断,直接返回。另一个是当最后一个元素对应的缺失元素个数小于k的时候,可以在a序列的后面加入一个非常大的虚拟值,这样就能保证找到一个大于等于k的缺失元素个数
3、代码实现
方法一:
class Solution {
public int findKthPositive(int[] arr, int k) {
if (arr[0] > k) {
return k;
}
int l = 0, r = arr.length;
while (l < r) {
int mid = (l + r) >> 1;
int x = mid < arr.length ? arr[mid] : Integer.MAX_VALUE;
if (x - mid - 1 >= k) {
r = mid;
} else {
l = mid + 1;
}
}
return k - (arr[l - 1] - (l - 1) - 1) + arr[l - 1];
}
}
方法二:
class Solution {
public int findKthPositive(int[] arr, int k) {
if(arr[0] > k){
return k;
}
int left = 0;
int right = arr.length;
while(left < right){
int mid = left + (right -left) / 2;
int x = mid < arr.length ? arr[mid] : Integer.MAX_VALUE;
if(x - mid - 1 >= k){
right = mid;
}
else{
left = mid + 1;
}
}
return k - (arr[right-1] - (right-1) - 1) + arr[right-1];
}
}