The KTH largest element in the array

Category Difficulty Likes Dislikes
algorithms Medium (57.96%). 225
Tags

divide-and-conquer | heap

Companies

Example 1:

Input: [3,2,1,5,6,4] and k = 2 output: 5Copy the code

Example 2:

Input: [3,2,3,1,2,4,5,5,6] and k = 4 output: 4Copy the code

Description:

You can assume that k is always valid, and that 1 ≤ k ≤ the length of the array.

/* * @lc app=leetcode.cn id=215 lang=javascript * * the KTH largest element in the array [215] */
/** * @param {number[]} nums * @param {number} k * @return {number} */
var findKthLargest = function(nums, k) {};Copy the code

1 all sorts

This is probably the easiest way to think about it

var findKthLargest = function (nums, k) {
  nums = nums.sort((a, b) = > b - a)
  return nums[k - 1]};Copy the code

Time complexity:

Space complexity:

But obviously, that’s not a good idea, so let’s say the array is 1W, and we just take the third one, and that’s embarrassing

2 part sorting (bubbling)

So, we’re thinking, just sort the first k numbers

var findKthLargest = function (nums, k) {
  for (let i = 0; i < k; i++) {
    for (let j = 0; j < nums.length - 1 - i; j++) {
      if (nums[j] > nums[j + 1])
        [nums[j], nums[j + 1]] = [nums[j + 1], nums[j]]
    }
  }
  return nums[nums.length - k]
};
Copy the code

So the time complexity is zero

3 part sort (heap)

If we want the KTH element, we don’t have to sort the first k elements

At this point, a lot of people think, right? How do we know who the KTH is if we don’t know what the first k numbers are?

That brings us to the concept of a heap. If we create a minimum heap of k elements, the heap must be smaller than any element in the heap

var findKthLargest = function (nums, k) {
  let minHeap = new MinHeap()
  for (let i = 0; i < nums.length; i++) {
    if (minHeap.size() < k) minHeap.push(nums[i])
    else if (minHeap.top() < nums[i]) {
      minHeap.pop()
      minHeap.push(nums[i])
    }
  }
  return minHeap.top()
};
Copy the code

So the time complexity can be reducedBut using the heap structure gives you a certain amount of extra space, and the space complexity is

Of course, the heap structure here has to be implemented by itself

If you don’t know how to write it you can look at my heap and heap sort

class MinHeap {
  constructor() {
    this.heap = []
    this.len = 0
  }
  size() {
    return this.len
  }
  push(val) {
    this.heap[++this.len] = val
    this.swin(this.len)
  }

  pop() {
    const ret = this.heap[1]
    this.swap(1.this.len--)
    this.heap[this.len + 1] = null
    this.sink(1)
    return ret
  }
  swin(ind) {
    while (ind > 1 && this.less(ind, parseInt(ind / 2))) {
      this.swap(ind, parseInt(ind / 2))
      ind = parseInt(ind / 2)
    }
  }
  sink(ind) {
    while (ind * 2< =this.len) {
      let j = ind * 2
      if (j < this.len && this.less(j + 1, j)) j++
      if (this.less(ind, j)) break
      this.swap(ind, j)
      ind = j
    }
  }
  top() {
    return this.heap[1]
  }
  isEmpty() {
    return this.len === 0
  }

  swap(i, j) {
    [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]]
  }
  less(i, j) {
    return this.heap[i] < this.heap[j]
  }
}
Copy the code

4 Quick Selection

In fact, when faced with a problem to find a certain element that matches, that is, the search problem

Binary search often comes to mind immediately

function binarySearch(arr, val) {
  let low = 0
  let high = arr.length - 1
  while (low <= high) {
    // mid indicates the middle value
    let mid = Math.floor(low + (high - low) / 2)
    if (arr[mid] === val) return mid
    val < arr[mid] ? high = mid - 1 : low = mid + 1
  }
  return null
}

let arr = [0.1.2.3.4.5]
console.log(binarySearch(arr, 0));
Copy the code

Binary search is based on the premise that arr[mid] is a median value so that the other half can be excluded

With the median, we have to think of quicksort Pivot

The partition function is a perfect match for binary lookup

function quickSort(arr) {
  return quick(arr, 0, arr.length - 1)}function quick(arr, left, right) {
  if (arr.length > 1) {
    let index = partition(arr, left, right)
    if (left < index - 1) quick(arr, left, index - 1)
    if (index < right) quick(arr, index, right)
  }
  return arr
}
function partition(arr, left, right) {
  const pivot = arr[Math.floor(left + (right - left) / 2)]
  let i = left
  let j = right
  while (i <= j) {
    while (arr[i] < pivot) i++
    while (pivot < arr[j]) j--
    if(i <= j) { [arr[i], arr[j]] = [arr[j], arr[i]] i++; j--; }}return i
}
let arr = [11.2.33.4.5]
console.log(quickSort(arr));
Copy the code

If you don’t understand quicksort, you can read my sort evolution (3) : Fast

But this way the pivot will be in the left array, not between right and left

So what we need to do is change the partition so that the pivot value comes out

function partition(arr, left, right) {
  let mid = Math.floor(left + (right - left) / 2)
  const pivot = arr[mid]
  // Place pivot at the end of arR
  [arr[mid], arr[right]] = [arr[right], arr[mid]]
  let i = left
  // Exclude pivot and do not sort pivot
  let j = right - 1
  while (i <= j) {
    while (arr[i] < pivot) i++
    while (pivot < arr[j]) j--
    if(i <= j) { [arr[i], arr[j]] = [arr[j], arr[i]] i++; j--; }}// Since arr[I] belongs to the left,pivot also belongs to the left
  // So we can swap the protected pivot with the middle value of the current array
  [arr[right], arr[i]] = [arr[i], arr[right]]
  return i
}
Copy the code

Now we just need to add the partition above in the binary search can achieve fast search

But the main thing is, do we want ascending or descending?

We’re looking for the KTH largest element

So the order of the array should be

Arr = [5,4,3,2,1]

For example, in the array above, the second largest number is 4, arr[1]

Our partition should be in reverse order, so the above code should be changed to

while (i <= j) {
+ while (arr[i] > pivot) i++
- while (arr[i] < pivot) i++
+ while (pivot < arr[j]) j--
- while (pivot < arr[j]) j--if (i <= j) { [arr[i], arr[j]] = [arr[j], arr[i]] i++; j--; }}Copy the code

Arr [I] is the k-1 bit of the partition, because the subscript is 0 and the first bit is I === 0

while (low <= high) {
  const mid = partition(arr.low, high)
  if (mid === k - 1) return arr[mid]

}
Copy the code

Arr [mid+1]~arr[high] if mid < k -1, arr[mid+1]~arr[high

Arr [low] ~ arr[mid – 1] arr[mid] ~ arr[mid – 1

So the code is

while (low <= high) {
  const mid = partition(nums, low, high)
  if (mid === k - 1) return nums[mid]
  mid < k - 1 ? low = mid + 1 : high = mid - 1
}
Copy the code

The final code is

/* * @lc app=leetcode.cn id=215 lang=javascript * * the KTH largest element in the array [215] */
/** * @param {number[]} nums * @param {number} k * @return {number} */
var findKthLargest = function (nums, k) {
  let low = 0
  let high = nums.length - 1
  while (low <= high) {
    const mid = partition(nums, low, high)
    if (mid === k - 1) return nums[mid]
    mid < k - 1 ? low = mid + 1 : high = mid - 1}}function partition(arr, low, high) {
  let mid = Math.floor(low + (high - low) / 2)
  const pivot = arr[mid]; // Remember to add a semicolon here
  // Place pivot at the end of arR
  [arr[mid], arr[high]] = [arr[high], arr[mid]]
  let i = low
  // Exclude pivot and do not sort pivot
  let j = high - 1
  while (i <= j) {
    while (arr[i] > pivot) i++
    while (arr[j] < pivot) j--
    if(i <= j) { [arr[i], arr[j]] = [arr[j], arr[i]] i++; j--; }}// Since arr[I] belongs to the left,pivot also belongs to the left
  // So we can swap the protected pivot with the middle value of the current array
  [arr[high], arr[i]] = [arr[i], arr[high]]
  return i
}
Copy the code

Complexity analysis

  • Time complexity: averageWorst-case scenario
  • Space complexity: