给你一个 严格升序排列 的正整数数组 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 。
提示:
1 <= arr.length <= 1000
1 <= arr[i] <= 1000
1 <= k <= 1000
对于所有 1 <= i < j <= arr.length
的 i
和 j
满足 arr[i] < arr[j]
进阶:
你可以设计一个时间复杂度小于 O(n) 的算法解决此问题吗?
方法一:枚举 思路与算法
我们可以顺序枚举。
用一个变量 current 表示当前应该出现的数,从 1 开始,每次循环都让该变量递增。用一个指针 ptr 指向数组中没有匹配的第一个元素,每轮循环中将该元素和 current 进行比较,如果相等,则指针后移,否则指针留在原地不动,说明缺失正整数 current。我们用 missCount 变量记录缺失的正整数的个数,每次发现有正整数缺失的时候,该变量自增,并且记录这个缺失的正整数,直到我们找到第 k 个缺失的正整数。
代码
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int findKthPositive (vector<int >& arr, int k) { int missCount = 0 , lastMiss = -1 , current = 1 , ptr = 0 ; for (missCount = 0 ; missCount < k; ++current) { if (current == arr[ptr]) { ptr = (ptr + 1 < arr.size ()) ? ptr + 1 : ptr; } else { ++missCount; lastMiss = current; } } return lastMiss; } };
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public int findKthPositive (int [] arr, int k) { int missCount = 0 , lastMiss = -1 , current = 1 , ptr = 0 ; for (missCount = 0 ; missCount < k; ++current) { if (current == arr[ptr]) { ptr = (ptr + 1 < arr.length) ? ptr + 1 : ptr; } else { ++missCount; lastMiss = current; } } return lastMiss; } }
[sol1-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Solution { public int FindKthPositive (int [] arr, int k ) { int missCount = 0 , lastMiss = -1 , current = 1 , ptr = 0 ; for (missCount = 0 ; missCount < k; ++current) { if (current == arr[ptr]) { ptr = (ptr + 1 < arr.Length) ? ptr + 1 : ptr; } else { ++missCount; lastMiss = current; } } return lastMiss; } }
[sol1-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 var findKthPositive = function (arr, k ) { let missCount = 0 , lastMiss = -1 , current = 1 , ptr = 0 ; for (missCount = 0 ; missCount < k; ++current) { if (current == arr[ptr]) { ptr = (ptr + 1 < arr.length ) ? ptr + 1 : ptr; } else { ++missCount; lastMiss = current; } } return lastMiss; };
[sol1-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution : def findKthPositive (self, arr: List [int ], k: int ) -> int : missCount = 0 lastMiss = -1 current = 1 ptr = 0 while missCount < k: if current == arr[ptr]: if ptr + 1 < len (arr): ptr += 1 else : missCount += 1 lastMiss = current current += 1 return lastMiss
复杂度分析
方法二:二分查找 思路与算法
对于每个元素 a_i,我们都可以唯一确定到第 i 个元素为止缺失的元素数量为 a_i - i - 1,例如:
第 i 个元素
a_i 的值
到第 i 个元素为止缺失的元素数量 p_i
a_0
2
1
a_1
3
1
a_2
4
1
a_3
7
3
a_4
11
6
我们发现 p_i 是随 i 非严格递增的,于是可以使用二分查找解决这个问题。我们只要找到一个 i 使得 p_{i - 1} < k \leq p_{i,就可以确定缺失的第 k 个数为 k - p_{i - 1} + a_{i - 1。也就是说,我们要找到第一个大于等于 k 的 p_i。
在实现的时候,我们要注意两个边界的处理:
当 a_0 > k 时,最终 i = 0,找不到 i - 1,所以提前判断是否 a_0 > k,如果是,则直接返回 k。
当最后一个元素对应的缺失个数 p_{n - 1} < k 时,我们并不能找到第一个大于等于 k 的 p_i,为了解决这个问题,可以在 a 序列的最后加入一个虚拟的值,这个值的大小为一个不会出现的非常大的数,这样就可以保证一定能找到一个大于等于 k 的 p_i。
代码
[sol2-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int findKthPositive (vector<int >& arr, int k) { if (arr[0 ] > k) { return k; } int l = 0 , r = arr.size (); while (l < r) { int mid = (l + r) >> 1 ; int x = mid < arr.size () ? arr[mid] : INT_MAX; if (x - mid - 1 >= k) { r = mid; } else { l = mid + 1 ; } } return k - (arr[l - 1 ] - (l - 1 ) - 1 ) + arr[l - 1 ]; } };
[sol2-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 ]; } }
[sol2-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public 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] : int .MaxValue; if (x - mid - 1 >= k) { r = mid; } else { l = mid + 1 ; } } return k - (arr[l - 1 ] - (l - 1 ) - 1 ) + arr[l - 1 ]; } }
[sol2-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var findKthPositive = function (arr, k ) { if (arr[0 ] > k) { return k; } let l = 0 , r = arr.length ; while (l < r) { const mid = Math .floor ((l + r) / 2 ); let x = mid < arr.length ? arr[mid] : 2000000 ; if (x - mid - 1 >= k) { r = mid; } else { l = mid + 1 ; } } return k - (arr[l - 1 ] - (l - 1 ) - 1 ) + arr[l - 1 ]; };
[sol2-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution : def findKthPositive (self, arr: List [int ], k: int ) -> int : if arr[0 ] > k: return k l, r = 0 , len (arr) while l < r: mid = (l + r) >> 1 x = arr[mid] if mid < len (arr) else 10 **9 if x - mid - 1 >= k: r = mid else : l = mid + 1 return k - (arr[l - 1 ] - (l - 1 ) - 1 ) + arr[l - 1 ]
复杂度分析