LeetCode 1539. 第 k 个缺失的正整数
题目描述


题意分析
给定严格递增的正整数数组,从所有正整数
1, 2, 3, ...中去掉数组里已经出现的数,找剩下序列中的第k个,排名从1开始。缺失统计从正整数
1开始,不是从数组首项开始。答案可能小于首项、位于相邻元素之间,也可能大于末项,不需要限制在数组原有数值范围内。
解法:二分缺失数量边界
核心思路
[!blue]
到数组下标
i为止,区间[1, arr[i]]一共有arr[i]个正整数,其中数组已出现i + 1个,所以缺失数量是missing(i) = arr[i] - i - 1。因为arr[i]本身存在,这也就是它之前的缺失数量。数组严格递增,相邻值至少增加一,而下标也只增加一,因此这个缺失数量不会下降。可以二分查找第一个满足
missing(i) >= k的位置,记为left,而不用逐个枚举缺失的正整数。这个边界的含义是:它前面的数组元素之前还没有缺够
k个数,因此这些元素都小于答案;从这个位置开始,已经有至少k个数缺失,所以这些元素都大于答案。若没有满足条件的位置,就让left = n,表示答案大于全部数组元素。因而答案之前恰好有
left个已经出现的正整数。设答案为x,从1到x中包含k个缺失数和left个存在数,两类刚好构成全部x个正整数,所以x = k + left。这个公式同时覆盖答案在首项之前或末项之后的情况。
解题步骤
- 使用左闭右开的二分区间,令
left = 0、right = arr.length。- 取中点
mid,计算它之前的缺失数量arr[mid] - mid - 1。- 若不足
k,中点及其左侧都不是目标边界,令left = mid + 1。- 否则中点已经满足条件,保留它并继续向左查找,令
right = mid。- 两边相遇后,返回
left + k,不需要读取可能等于数组长度的arr[left]。
代码实现
class Solution {
public int findKthPositive(int[] arr, int k) {
int left = 0;
// 右开边界允许最终位置等于数组长度,循环不会读取这个边界下标。
int right = arr.length;
while (left < right) {
int mid = left + (right - left) / 2;
// 无缺失时 arr[mid] 应为 mid + 1,差值就是前面缺了几个正整数。
int missing = arr[mid] - mid - 1;
if (missing < k) {
left = mid + 1;
} else {
// mid 本身就是候选,收缩时必须保留它。
right = mid;
}
}
// left 是答案之前仍然存在的数的个数,答案即第 left + k 个正整数。
return left + k;
}
}
func findKthPositive(arr []int, k int) int {
left := 0
// 右开边界允许最终位置等于数组长度,循环不会读取这个边界下标。
right := len(arr)
for left < right {
mid := left + (right-left)/2
// 无缺失时 arr[mid] 应为 mid + 1,差值就是前面缺了几个正整数。
missing := arr[mid] - mid - 1
if missing < k {
left = mid + 1
} else {
// mid 本身就是候选,收缩时必须保留它。
right = mid
}
}
// left 是答案之前仍然存在的数的个数,答案即第 left + k 个正整数。
return left + k
}
复杂度分析
- 时间复杂度:$O(\log(n+1))$。
- 空间复杂度:$O(1)$。
关键点总结
[!green]
- 数值减去已出现数量,得到的是累计缺失数量,它具有二分需要的单调性。
- 寻找第一个达到
k的边界,才能知道答案之前有多少个已存在元素。k + left来自存在与缺失两类正整数的计数,不依赖答案位于某个数组元素附近。
易错点总结
[!yellow]
- 缺失数量写成
arr[i] - i,忘了下标i对应已经出现i + 1个数,会多算一个。- 缺失数量等于
k时立即返回中点,可能落在一段相同计数的后部,应继续找第一个满足位置。- 将右边界限制为最后一个真实下标后又强行读取结果,会漏掉答案超过末项的情况。
- 返回
arr[left],得到的是一个已经存在的数,而且left = n时还会越界。- 在允许重复的数组上照搬计数式,会把重复项当成不同的已出现正整数,前提不再成立。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 1060. 有序数组中的缺失元素 | 中等 | 本题从正整数1开始计缺失值,原题以数组首项为起点,前缀缺失计数基准不同。 |
| 163. 缺失的区间 | 简单 | 缺失值组成有序间隙,本题按每个间隙的大小跳过,定位第k项而非输出全部区间。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!