215. Kth largest Element in an Array4 C + + Solutions using Partition, Max-heap, Priority_queue and Multiset respectively
Well, this problem have a naive solution, which is to sort the array in descending order and return the k-1 -th element.
class Solution {public: int findKthLargest(vector<int>& nums, int k) { sort(nums.begin(), nums.end()); return nums[k - 1]; }};
However, sorting algorithm gives O(nlogn) complexity. Suppose n = 10000 k = 2 and, then we is doing a lot of unnecessary operations. In fact, this problem have at least and faster solutions.
Well, the faster solution has no mystery. It is also closely related to sorting. I'll give II algorithms for this problem below, one using quicksort (specifically, the Partition subroutine) and the Oth Er using Heapsort.
Quicksort
In Quicksort, with each iteration, we need to select a pivot and then partition the array into three parts:
- Elements smaller than the pivot;
- Elements equal to the pivot;
- Elements larger than the pivot.
Now, let's do a example with the array in the [3, 2, 1, 5, 4, 6] problem statement. Let's assume in each time we select the leftmost element to is the pivot, in this case, 3 . We then use it to partition the array into the above 3 parts, which results in [1, 2, 3, 5, 4, 6] . Now are in the 3 third position and we know, it is the third smallest element. Now, does you recognize so this subroutine can is used to solve this problem?
In fact, the above partition puts elements smaller than the pivot before the pivot and thus the pivot would then be thek-th smallest element If it is in thek-1-th position. Since the problem requires us to find thek-th largest element, we can simply modify the partition to put elements larger than the pivot before the pivot. That's, after partition, the array becomes[5, 6, 4, 3, 1, 2]. Now we know that3is the4-th largest element. If we are asked to find the2-th largest element, then we know it's left to3. If we are asked to find the5-th largest element, then we know it's right to3. So, in the average sense, the problem are reduced to approximately half of its original size, giving the recursionT(n) = T(n/2) + O(n)in whichO(n)is the time for partition. This recursion, once solved, givesT(n) = O(n)And thus we have a linear time solution. Note that since we have need to consider one half of the array, the time complexity isO(n). If we need to consider both the halves of the arrays, like quicksort and then the recursion would beT(n) = 2T(n/2) + O(n)And the complexity would beO(nlogn).
Of course, is the O(n) average time complexity. In the worst case, the recursion may become and the T(n) = T(n - 1) + O(n) complexity would be O(n^2) .
Now let's briefly write down the algorithm before writing our codes.
- Initialize to being 0 and to is
left right nums.size() - 1 ;
- Partition the array, if the pivot is
k-1 at the-th position, return it (we were done);
- If
k-1 The pivot is right-to-the-th position, update to being the left neighbor of the right pivot;
- Else update to being the right neighbor of the
left pivot.
- Repeat 2.
Now let's turn it into code.
Class Solution {Publicint partition (vector<int>& Nums,IntLeftIntright) {int pivot = nums[Left];int L =Left +1, R =Rightwhile (L <= R) {if (Nums[l] < pivot && Nums[r] > Pivot) swap (nums[l++], nums[r--]);if (Nums[l] >= pivot) l++;if (Nums[r] <= pivot) r--; } Swap (nums[Left], nums[r]); return R; }int Findkthlargest (vector<int>& nums, int k) { int left = 0, right = Nums.size ()- 1; while (true) { Int. pos = partition (Nums, left , right ); if (pos = = k- 1) return Nums[pos]; if (pos > K- 1) Right = pos- 1; Else Left = pos + 1; }};
Heapsort
Well, this problem still have a tag "heap". If you is familiar with heapsort, you can solve this problem using the following idea:
- Build a max-heap
nums for, set to is heap_size nums.size() ;
- Swap
nums[0] (after buding the max-heap, it'll be is the largest element) with nums[heap_size - 1] (currently the last element). Then decrease by heap_size 1 and max-heapify nums (recovering it max-heap property) at index 0 ;
- Repeat 2 for Times and the
k k -th largest element would be a stored finally at nums[heap_size] .
Now I paste my code below. If you find it tricky, I suggest your to read the Heapsort chapter of Introduction to algorithms, which have a nice explanat Ion of the algorithm. My code simply translates the pseudo code in this book:-)
Class Solution {PublicInlineIntLeft(int idx) {Return (IDX <<1) +1; }InlineIntRight(int idx) {Return (IDX <<1) +2; }voidMax_heapify(vector<int>& Nums,int idx) {int largest = IDX;int L = Left (idx), R = Right (IDX);if (L < heap_size && Nums[l] > nums[largest]) largest = l;if (R < heap_size && Nums[r] > nums[largest]) largest = R;if (Largest! = idx) {swap (Nums[idx], nums[largest]); max_heapify (nums, largest);}}voidBuild_max_heap(vector<int>& nums) {heap_size = Nums.size ();for (int i = (heap_size >> 1)-1; i >= 0; i--) max_heapify (nums, i); } int findkthlargest (vector<int>& nums, int k" {build_max_heap (nums); for (int i = 0; i < K; i++) {Swap ( Nums[0], nums[heap_size-1]); heap_size--; Max_heapify (Nums, Span class= "Hljs-number" >0); } return nums[heap_size];} private: int heap_size;}
If We is allowed to use the built-in priority_queue , the code would be a much more shorter:-)
class solution {public: int Findkthlargest (vector<int>& nums, int k" {priority_queue<int> PQ (nums.begin (), Nums. End (for (int i = 0; i < K-1; i++) Pq.pop (); return pq.top ();}};
Well, the priority_queue can also be replaced by multiset :-)
class Solution {public: int findKthLargest(vector<int>& nums, int k) { multiset<int> mset; int n = nums.size(); for (int i = 0; i < n; i++) { mset.insert(nums[i]); if (mset.size() > k) mset.erase(mset.begin()); } return *mset.begin(); }};
Sort-leetcode "Sort"