$$ \newcommand\Tr{\mathrm{Tr}} \newcommand{\braket}[2]{\langle #1 \mid #2 \rangle} \newcommand\I{\mathbb{I}} \newcommand{\avg}[1]{\left< #1 \right>} \newcommand{\RD}{D} \newcommand{\ri}{\mathrm{i}} \DeclareMathOperator{\sign}{sign} \DeclareMathOperator{\Sign}{Sign} \newcommand{\ii}{\mathrm i} \newcommand{\vv}{\mathrm v} \newcommand{\ff}{\mathrm f} \newcommand{\mm}{\mathrm m} \newcommand{\ee}{\mathrm e} \newcommand{\xx}{\mathrm x} \newcommand{\RR}{\mathrm R} \newcommand{\dd}{\mathrm d} \newcommand{\FF}{\mathrm F} \newcommand{\BB}{\mathrm B} \newcommand{\vph}{v_{\mathrm{ph}}} $$

Kth Largest Element in an Array

Problem

Given an array of integers, our task is to find Kth Largest Element in the Array. The Kth largest element here is in the sorted order, not in the Kth distinct element.
For example,

Input: nums = [3,2,1,5,6,4], k = 2
Output: 5
Explanation: 5 is the second largest element in the sorted order.
Input: nums = [3,2,3,1,2,4,5,5,6], k = 4
Output: 4
Explanation: 4 is the fourth largest element in the sorted order.

Solution

Sorting

fun findKthLargest(nums: IntArray, k: Int): Int {
    nums.sort()
    return nums[nums.size - k]
}

public int findKthLargest(int[] nums, int k) {
    Arrays.sort(nums);
    return nums[nums.length - k];
}

Priority Queue (Heap) - Two Pass

fun findKthLargest(nums: IntArray, k: Int): Int {
    val priorityQueue = PriorityQueue(
        compareByDescending<Int> { it }
    )
    nums.forEach {
        priorityQueue.add(it)
    }

    var count = 1
    while (priorityQueue.isNotEmpty()) {
        val num = priorityQueue.poll()
        if (count == k)
            return num

        count++
    }

    return 0
}

public int findKthLargest(int[] nums, int k) {
    PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(
            Comparator.reverseOrder()
    );
    for (int num : nums) {
        priorityQueue.add(num);
    }

    var count = 1;
    while (!priorityQueue.isEmpty()) {
        int num = priorityQueue.poll();
        if (count == k)
            return num;

        count++;
    }

    return 0;
}

Priority Queue (Heap) - One Pass

fun findKthLargest(nums: IntArray, k: Int): Int {
    val priorityQueue = PriorityQueue(
        compareBy<Int> { it }
    )
    nums.forEach {
        if (priorityQueue.size < k)
            priorityQueue.add(it)
        else {
            if (priorityQueue.peek() < it) {
                priorityQueue.remove()
                priorityQueue.add(it)
            }
        }
    }

    return priorityQueue.peek()
}

public int findKthLargest(int[] nums, int k) {
    PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
    for (int num : nums) {
        if (priorityQueue.size() < k)
            priorityQueue.add(num);
        else {
            if (priorityQueue.peek() < num) {
                priorityQueue.remove();
                priorityQueue.add(num);
            }
        }
    }

    return priorityQueue.peek();
}

Quickselect

fun findKthLargest(nums: IntArray, k: Int): Int {
    return quickSelect(nums, k, 0, nums.size - 1) 
}

private fun quickSelect(nums: IntArray, k: Int, low: Int, high: Int): Int {
    var l = low
    var h = high
    val smallestElementIndex = nums.size - k
    while (l <= h) {
        if (l == h)
            return nums[l]
        val random = Random()
        var pivotIndex = l + random.nextInt(h - l)
        pivotIndex = partition(nums, l, h, pivotIndex)
        if (pivotIndex == smallestElementIndex)
            return nums[pivotIndex]
        else if (smallestElementIndex < pivotIndex)
            h = pivotIndex - 1
        else
            l = pivotIndex + 1
    }

    return -1
}

private fun partition(nums: IntArray, low: Int, high: Int, pivotIndex: Int): Int {
    val pivot = nums[pivotIndex]

    nums[pivotIndex] = nums[high].also {
        nums[high] = nums[pivotIndex]
    }

    var storeIndex = low

    for (i in low until high) {
        if (nums[i] < pivot) {
            nums[storeIndex] = nums[i].also {
                nums[i] = nums[storeIndex]
            }

            storeIndex++
        }
    }

    nums[storeIndex] = nums[high].also {
        nums[high] = nums[storeIndex]
    }

    return storeIndex
}

public int findKthLargest(int[] nums, int k) {
    return quickSelect(nums, k, 0, nums.length - 1);
}

private int quickSelect(int[] nums, int k, int low, int high) {
    int smallestElementIndex = nums.length - k;
    while (low <= high) {
        if (low == high)
            return nums[low];
        Random random = new Random();
        var pivotIndex = low + random.nextInt(high - low);
        pivotIndex = partition(nums, low, high, pivotIndex);
        if (pivotIndex == smallestElementIndex)
            return nums[pivotIndex];
        else if (smallestElementIndex < pivotIndex)
            high = pivotIndex - 1;
        else
            low = pivotIndex + 1;
    }

    return -1;
}

private int partition(int[] nums, int low, int high, int pivotIndex) {
    int pivot = nums[pivotIndex];

    swap(nums, high, pivotIndex);

    int storeIndex = low;

    for (int i = low; i < high; i++) {
        if (nums[i] < pivot) {
            swap(nums, storeIndex, i);
            storeIndex++;
        }
    }

    swap(nums, storeIndex, high);

    return storeIndex;
}

private void swap(int[] nums, int a, int b) {
    int temp = nums[b];
    nums[b] = nums[a];
    nums[a] = temp;
}