leetcode.com/problems/kth-missing-positive-number/
O(N)과 O(lgN)이 존재하므로 흥미롭다고 생각되는 문제
Easy
54918Add to ListShare
Given an array arr of positive integers sorted in a strictly increasing order, and an integer k.
Find the kth positive integer that is missing from this array.
Example 1:
Input: arr = [2,3,4,7,11], k = 5 Output: 9 Explanation: The missing positive integers are [1,5,6,8,9,10,12,13,...]. The 5th missing positive integer is 9.
Example 2:
Input: arr = [1,2,3,4], k = 2 Output: 6 Explanation: The missing positive integers are [5,6,7,...]. The 2nd missing positive integer is 6.
Constraints:
Keep track of how many positive numbers are missing as you scan the array.
The first idea is to solve the problem in a linear time by parsing all array elements. It's easy to say how many positive numbers are missing between two array elements: arr[i + 1] - arr[i] - 1, and we could use it to solve the problem.
Figure 1. Approach 1. Iterate over the array and compute the number of missing numbers in-between the elements.
Algorithm
Check if the kth missing number is less than the first element of the array. If it's the case, return k.
Decrease k by the number of positive integers which are missing before the array starts: k -= arr[0] - 1.
Iterate over the array elements:
At each step, compute the number of missing positive integers in-between i + 1th and ith elements: currMissing = arr[i + 1] - arr[i] - 1.
Compare k to the currMissing. If k <= currMissing then the number to return is in-between arr[i + 1] and arr[i], and you could return it: arr[i] + k.
Otherwise, decrease k by currMissing and proceed further.
We're here because the element to return is greater than the last element of the array. Return it: arr[n - 1] + k.
Implementation
class Solution:
def findKthPositive(self, arr: List[int], k: int) -> int:
# if the kth missing is less than arr[0]
if k <= arr[0] - 1:
return k
k -= arr[0] - 1
# search kth missing between the array numbers
for i in range(len(arr) - 1):
# missing between arr[i] and arr[i + 1]
curr_missing = arr[i + 1] - arr[i] - 1
# if the kth missing is between
# arr[i] and arr[i + 1] -> return it
if k <= curr_missing:
return arr[i] + k
# otherwise, proceed further
k -= curr_missing
# if the missing number if greater than arr[-1]
return arr[-1] + k
Complexity Analysis
Time complexity: \mathcal{O}(N)O(N) because in the worst case, we have to parse all array elements.
Space complexity: \mathcal{O}(1)O(1), we don't allocate any additional data structures.
Approach 1 doesn't profit from the fact that the input array is sorted. And the sorted input should ring the bell: "let's try to solve it in a logarithmic time using binary search".
We need a way to check on how many positive integers are missing before the given array element to use binary search. To do that, let's compare the input array [2, 3, 4, 7, 11] with an array with no missing integers: [1, 2, 3, 4, 5]. The number of missing integers is a simple difference between the corresponding elements of these two arrays:
Before 2, there is 2 - 1 = 1 missing integer.
Before 3, there is 3 - 2 = 1 missing integer.
Before 4, there is 4 - 3 = 1 missing integer.
Before 7, there are 7 - 4 = 3 missing integers.
Before 11, there are 11 - 5 = 6 missing integers.
The number of positive integers which are missing before the arr[idx] is equal to arr[idx] - idx - 1.
Figure 2. The number of missing positive integers before the index idx.
Now we have everything to proceed with the classical binary search algorithm:
Choose the pivot index in the middle of the array.
If the number of positive integers which are missing before arr[pivot] is less than k - continue to search on the right side of the array.
Otherwise, continue to search on the left side.
Algorithm
Initialize search boundaries: left = 0, right = arr.length - 1.
While left <= right:
Choose the pivot index in the middle: pivot = left + (right - left) / 2. Note that in Java we couldn't use straightforward pivot = (left + right) / 2 to avoid the possible overflow. In Python, the integers are not limited, and we're fine to do that.
If the number of positive integers which are missing before is less than k arr[pivot] - pivot - 1 < k - continue to search on the right side of the array: left = pivot + 1.
Otherwise, continue to search on the left: right = pivot - 1.
At the end of the loop, left = right + 1, and the kth missing number is in-between arr[right] and arr[left]. The number of integers missing before arr[right] is arr[right] - right - 1. Hence, the number to return is arr[right] + k - (arr[right] - right - 1) = k + left.
Implementation
class Solution:
def findKthPositive(self, arr: List[int], k: int) -> int:
left, right = 0, len(arr) - 1
while left <= right:
pivot = (left + right) // 2
# If number of positive integers
# which are missing before arr[pivot]
# is less than k -->
# continue to search on the right.
if arr[pivot] - pivot - 1 < k:
left = pivot + 1
# Otherwise, go left.
else:
right = pivot - 1
# At the end of the loop, left = right + 1,
# and the kth missing is in-between arr[right] and arr[left].
# The number of integers missing before arr[right] is
# arr[right] - right - 1 -->
# the number to return is
# arr[right] + k - (arr[right] - right - 1) = k + left
return left + k
Complexity Analysis
Time complexity: \mathcal{O}(\log N)O(logN), where NN is a number of elements in the input array.
Let's compute time complexity of the binary search with the help of master theorem T(N) = aT\left(\frac{N}{b}\right) + \Theta(N^d)T(N)=aT(bN)+Θ(Nd). The equation represents dividing the problem up into aa subproblems of size \frac{N}{b}bN in \Theta(N^d)Θ(Nd) time. Here, at each step, there is only one subproblem a = 1, its size is a half of the initial problem b = 2, and all this happens in a constant time d = 0. That means that \log_b{a} = dlogba=d and hence we're dealing with case 2 that results in \mathcal{O}(N^{\log_b{a}} \log^{d + 1} N)O(Nlogbalogd+1N) = \mathcal{O}(\log N)O(logN) time complexity.
Space complexity: \mathcal{O}(1)O(1), we don't allocate any additional data structures.