The kth largest element in the array

Posted by McManCSU on Fri, 11 Feb 2022 03:38:52 +0100

Topic introduction

Question 215: https://leetcode-cn.com/problems/kth-largest-element-in-an-array/

The kth largest element was found in an unordered array. Note that you need to find the k-th element of the array instead of the largest element.

analysis

To find the k-largest element in the array, the first thing you can think of is, of course, direct sorting. As long as the array is ordered, then take out the penultimate K element.

public class KthLargestElement {
    // Sorting method built in direct call language
    public int findKthLargest(int[] nums, int k) {
        Arrays.sort(nums);
        return nums[nums.length - k];
    }
}

As we know, java arrays The bottom layer of sort () method is quick sorting, so the time complexity is O(nlogn). If you actually encounter this problem, it is obviously not satisfactory for the interviewer to sort directly with the method of tone library. We should write the sorting algorithm manually.

The time complexity of selection, bubbling and insertion sorting is O(n^2), and the performance is poor; Two count sorting, bucket sorting and cardinal sorting require a lot of extra space despite their low time complexity, and are more efficient only when the data value range is relatively centralized and the number of buckets is small. Therefore, in practical application, the implementation algorithm of sorting generally adopts quick sorting, or merging and heap sorting.

For this topic, it can be further optimized: because we only care about the K-largest element, the elements in other positions can not be arranged. Based on this idea, it is obvious that the algorithm of merging cannot be optimized; But fast sorting and heap sorting can.

Method 1: selection based on quick sort

We can improve the quick sort algorithm to solve this problem: in the process of partition, we will divide the subarray. If the position Q obtained by the division is exactly the subscript we need, we will directly return a[q]; Otherwise, if q is smaller than the target subscript, the right sub interval is recursive, otherwise the left sub interval is recursive. In this way, the original recursive two intervals can be changed into only one interval, which improves the time efficiency. This is the "quick selection" algorithm. In addition, we know that the performance of quick sort is closely related to the length of the subarray. We can introduce randomization to speed up this process. The expectation of its time cost is O(n).

public int findKthLargest(int[] nums, int k) {
    return quickSelect( nums, 0, nums.length - 1, nums.length - k );
}
// To facilitate recursion, a quick selection method is defined
public int quickSelect( int[] nums, int start, int end, int index ){
    int q = randomPatition( nums, start, end );
    if (q == index){
        return nums[q]; 
    } else {
        return q > index ? quickSelect(nums, start, q - 1, index) : quickSelect(nums, q + 1, end, index);
    }
}
// Define a random partition method
public int randomPatition( int[] nums, int start, int end ){
    Random random = new Random();
    int randIndex = start + random.nextInt(end - start + 1); 
    swap(nums, start, randIndex);
    return partition(nums, start, end);
}
// Define a partition method
public int partition( int[] nums, int start, int end ){
    int pivot = nums[start];
    int left = start; 
    int right = end; 
    while ( left < right ){
        while ( left < right && nums[right] >= pivot )
            right --;
        nums[left] = nums[right];
        while ( left < right && nums[left] <= pivot )
            left ++;
        nums[right] = nums[left];
    }
    nums[left] = pivot;
    return left;
}
// Defines a method for exchanging elements
public void swap( int[] nums, int i, int j ){
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

Complexity analysis

  • Time complexity: O(n). For the proof process, please refer to 9.2: selection algorithm with linear expectation in introduction to algorithm.
  • Space complexity: O(logn). The space cost of recursive use of stack space is expected to be O(logn).

Method 2: selection based on heap sorting

We can also use heap sorting to solve this problem. The basic idea is to build a large top heap. After k − 1 deletion, the top element of the heap is the answer we are looking for.

In many languages, there are priority queues or heap containers that can be used directly, but in interviews, interviewers prefer to let interviewers implement a heap themselves. So here we need to manually implement an implementation similar to heap sorting.
The code is shown as follows:

public int findKthLargest(int[] nums, int k) {
    int n = nums.length;
    int heapSize = n;
    // Construction of large top reactor
    buildMaxHeap( nums, heapSize );
    // Delete k-1 heap top elements
    for ( int i = n - 1; i > n - k; i-- ){
        swap( nums, 0, i );
        heapSize --;
        maxHeapify( nums, 0, heapSize );
    }
    return nums[0];
}

// Method of constructing large top pile
public void buildMaxHeap( int[] nums, int heapSize ){
    for ( int i = heapSize / 2 - 1; i >= 0; i-- ){
        maxHeapify(nums, i, heapSize);
    }
}
public void maxHeapify( int[] nums, int top, int heapSize ){
    int left = top * 2 + 1;
    int right = top * 2 + 2;
    int largest = top;
    if ( right < heapSize && nums[right] > nums[largest] ){
        largest = right;
    }
    if ( left < heapSize && nums[left] > nums[largest] ){
        largest = left;
    }
    if ( largest != top ){
        swap( nums, top, largest );
        maxHeapify(nums, largest, heapSize);
    }
}

Complexity analysis

  • Time complexity: O(nlogn), the time cost of heap building is O(n), and the total cost of k-1 deletion is O(klogn). Because K < n, the progressive time complexity is O(n+klogn)=O(nlogn).
  • Space complexity: O(logn), that is, the space cost of recursive use of stack space.

Topics: Java Algorithm