Welcome to Subscribe On Youtube
215. Kth Largest Element in an Array
Description
Given an integer array nums
and an integer k
, return the kth
largest element in the array.
Note that it is the kth
largest element in the sorted order, not the kth
distinct element.
Can you solve it without sorting?
Example 1:
Input: nums = [3,2,1,5,6,4], k = 2 Output: 5
Example 2:
Input: nums = [3,2,3,1,2,4,5,5,6], k = 4 Output: 4
Constraints:
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104
Solutions
Solution 1: Sorting
We can sort the array $nums$ in ascending order, and then get $nums[n-k]$.
The time complexity is $O(n \times \log n)$, where $n$ is the length of the array $nums$.
Solution 2: Partition
We notice that it is not always necessary for the entire array to be in an ordered state. We only need local order. That is to say, if the elements in the position $[0..k)$ are sorted in descending order, then we can determine the result. Here we use quick sort.
Quick sort has a characteristic that at the end of each loop, it can be determined that the $partition$ is definitely at the index position it should be. Therefore, based on it, we know whether the result value is in the left array or in the right array, and then sort that array.
The time complexity is $O(n)$, where $n$ is the length of the array $nums$.
Why Swap nums[lo]
with nums[r]
and Not nums[l]
?
The critical reason for using nums[lo], nums[r] = nums[r], nums[lo]
instead of nums[lo], nums[l] = nums[l], nums[lo]
in the partition
method is related to the guarantees provided by the partitioning process about the elements relative to the pivot’s final position.
Understanding the Partition Process:
The partition function’s goal is to rearrange elements in nums[lo:hi+1]
such that all elements greater than pivot
come before pivot
and all elements less than pivot
come after it. The final position of pivot
is then returned.
- Pivot Initialization: The
pivot
is chosen as the first element in the range (nums[lo]
). - Pointers
l
andr
Movement: The pointersl
(initialized tolo+1
) andr
(initialized tohi
) move towards each other.l
moves right past elements greater than or equal topivot
because they are correctly placed relative topivot
for adescending
sort (since we’re looking for the k-th largest element). Similarly,r
moves left past elements less than or equal topivot
. - Swapping
l
andr
: Ifl
points to an element less thanpivot
andr
points to an element greater thanpivot
, they are out of place for the intendeddescending
order, sonums[l]
andnums[r]
are swapped.
After the while loop terminates, the pointer l
has either moved past r
or is pointing to an element not less than pivot
(due to the condition that increments l
), and r
is pointing at an element that is less than or equal to pivot
(because r
decrements when nums[r] <= pivot
). Swapping pivot
with nums[r]
ensures that pivot
is placed just before the first element greater than itself encountered from the right. This is the correct final position of pivot
in a descending order partitioning since everything to the left of pivot
is greater or equal, and everything to the right of pivot
is less or equal.
Example Illustration:
Let’s consider an example with nums = [3, 5, 2, 1, 4]
and k = 2
(looking for the 2nd largest element).
- Initial call to
partition
: pivot = 3, nums = [3, 5, 2, 1, 4]. - After partitioning: nums could be rearranged to [4, 5, 3, 1, 2] with
r
pointing to3
. - Swapping
nums[lo]
withnums[r]
results in [3, 5, 4, 1, 2], where3
is correctly placed in its final position for a descending order sorting. The returned index would be where3
is placed, and since we’re looking for the k-th largest, we adjustlo
andhi
based on this index.
If you had swapped with nums[l]
instead, you could end up placing a number less than pivot
in pivot
’s initial position, breaking the partitioning invariant, or you might not properly position the pivot relative to all other elements, potentially causing incorrect behavior or infinite loops in the algorithm’s logic.
-
class Solution { public int findKthLargest(int[] nums, int k) { int n = nums.length; return quickSort(nums, 0, n - 1, n - k); } private int quickSort(int[] nums, int left, int right, int k) { if (left == right) { return nums[left]; } int i = left - 1, j = right + 1; int x = nums[(left + right) >>> 1]; while (i < j) { while (nums[++i] < x) ; while (nums[--j] > x) ; if (i < j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } } if (j < k) { return quickSort(nums, j + 1, right, k); } return quickSort(nums, left, j, k); } }
-
class Solution { public: int findKthLargest(vector<int>& nums, int k) { int n = nums.size(); return quickSort(nums, 0, n - 1, n - k); } int quickSort(vector<int>& nums, int left, int right, int k) { if (left == right) return nums[left]; int i = left - 1, j = right + 1; int x = nums[left + right >> 1]; while (i < j) { while (nums[++i] < x) ; while (nums[--j] > x) ; if (i < j) swap(nums[i], nums[j]); } return j < k ? quickSort(nums, j + 1, right, k) : quickSort(nums, left, j, k); } };
-
class Solution: def findKthLargest(self, nums: List[int], k: int) -> int: lo, hi = 0, len(nums) - 1 while lo <= hi: pos = self.partition(nums, lo, hi) if pos == k - 1: # pos starting from 0, so -1 return nums[pos] # partially sorted, desc elif pos > k - 1: hi = pos - 1 else: # pos < k - 1 lo = pos + 1 return -1 # or raise exception def partition(self, nums: List[int], lo: int, hi: int) -> int: pivot, l, r = nums[lo], lo + 1, hi while l <= r: if nums[l] < pivot and nums[r] > pivot: # larger num at left of pivot, easier to count for k-th lagest nums[l], nums[r] = nums[r], nums[l] l += 1 r -= 1 if nums[l] >= pivot: l += 1 if nums[r] <= pivot: r -= 1 # use nums[l] will lead to infinate looping nums[lo], nums[r] = nums[r], nums[lo] # possible there is duplicated num, but will be covered here return r ############## class Solution: def findKthLargest(self, nums: List[int], k: int) -> int: def quick_sort(left, right, k): if left == right: return nums[left] i, j = left - 1, right + 1 x = nums[(left + right) >> 1] while i < j: while 1: i += 1 if nums[i] >= x: break while 1: j -= 1 if nums[j] <= x: break if i < j: nums[i], nums[j] = nums[j], nums[i] if j < k: return quick_sort(j + 1, right, k) return quick_sort(left, j, k) n = len(nums) return quick_sort(0, n - 1, n - k) ############ class Solution: def findKthLargest(self, nums: List[int], k: int) -> int: def quick_sort(left, right, k): if left == right: return nums[left] i, j = left - 1, right + 1 x = nums[(left + right) >> 1] while i < j: while 1: i += 1 if nums[i] >= x: break while 1: j -= 1 if nums[j] <= x: break if i < j: nums[i], nums[j] = nums[j], nums[i] if j < k: return quick_sort(j + 1, right, k) return quick_sort(left, j, k) n = len(nums) return quick_sort(0, n - 1, n - k)
-
func findKthLargest(nums []int, k int) int { n := len(nums) return quickSort(nums, 0, n-1, n-k) } func quickSort(nums []int, left, right, k int) int { if left == right { return nums[left] } i, j := left-1, right+1 x := nums[(left+right)>>1] for i < j { for { i++ if nums[i] >= x { break } } for { j-- if nums[j] <= x { break } } if i < j { nums[i], nums[j] = nums[j], nums[i] } } if j < k { return quickSort(nums, j+1, right, k) } return quickSort(nums, left, j, k) }
-
function findKthLargest(nums: number[], k: number): number { const n = nums.length; const swap = (i: number, j: number) => { [nums[i], nums[j]] = [nums[j], nums[i]]; }; const sort = (l: number, r: number) => { if (l + 1 > k || l >= r) { return; } swap(l, l + Math.floor(Math.random() * (r - l))); const num = nums[l]; let mark = l; for (let i = l + 1; i < r; i++) { if (nums[i] > num) { mark++; swap(i, mark); } } swap(l, mark); sort(l, mark); sort(mark + 1, r); }; sort(0, n); return nums[k - 1]; }
-
use rand::Rng; impl Solution { pub fn find_kth_largest(mut nums: Vec<i32>, k: i32) -> i32 { let k = k as usize; let n = nums.len(); let mut l = 0; let mut r = n; while l <= k - 1 && l < r { nums.swap(l, rand::thread_rng().gen_range(l, r)); let num = nums[l]; let mut mark = l; for i in l..r { if nums[i] > num { mark += 1; nums.swap(i, mark); } } nums.swap(l, mark); if mark + 1 <= k { l = mark + 1; } else { r = mark; } } nums[k - 1] } }