Record 1 algorithm problem

The KTH large element in the array

Leetcode-cn.com/problems/kt…


Ps: Pile on another oneThe articleDetailed introduction. I don’t want to repeat it so you can skip over it. The heap code for this problem is going to be written right at the end.

The easiest way to do this is to sort and return the element with index k minus 1.

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

This seems too simple, we can do something by hand, for example, through the quick list, or pile to answer the questions.

1. The fast row

Quicksort is basically taking a reference point, and putting the number greater than it to the left and the number less than it to the right. Left or right depends on whether you want to go up or down.

This recursive method of cutting a big problem into smaller problems is called divide-and-conquer. The termination condition of recursion is that there is only one element in the interval, so it just returns.

But what we’re going to find is that as long as the first k is in descending order or ascending order, we know what the KTH largest element or the KTH smallest element is. So when the KTH number we want is to the left of the reference point, we recurse to the left of the array, and vice versa, and we cut the work in half. If the KTH happens to be the reference point, it just returns without doing any recursion.

[3, 2, 5, 6, 7] let's say our base point is 5, and then for the second largest element we just recurse 3, 2Copy the code

The intuitive way to write quicksort is to recursively generate two arrays at a time and concatenate them on return. Such as:

    / / pseudo code
    const left = []
    const right = []
    if (arr[i] > 基准点) {
        right.push(arr[i])
    } else {
        left.push(arr[i])
    }
    
    returnRecursion (left), reference point, recursion (right)Copy the code

Another space-complexity operation is to sort directly on the original array, but how to swap nodes is a critical and difficult problem. Is one of the main points of this article.

This is where Pointers come in, and to facilitate swapping, we need to set the reference point to the beginning of the array, arr[0].

Let’s say we have a swap function, swap, that passes in arrays and subscripts and swaps automatically.

You can always fix the first one in that array as your reference point, or you can pick a random number as your reference point, and you can use the random number as your reference point.

First, since we are operating on the original array, we only have the starting and ending indices of the interval, not necessarily 0 and arr.length-1.

    // Take the random subscript of this interval
    let pivotIdx = Math.floor(Math.random() * (right - left + 1)) + left
    const pivot = arr[pivotIdx]
    swap(arr, left, pivotIdx)
    pivotIdx = left
    left += 1

    /* * select * from 'a' where 'left' and 'right' are sorted. * [a, b, c, d, e, f] * left right */
Copy the code

Then, we use the for loop to iterate over the nodes in the interval, swapping when they are larger than A, and we need a new pointer that points to the boundary that can be swapped. We can use left here, because it doesn’t do anything else.

When we judge that it is larger than the reference point, we swap the I node of the current for loop with the left. After swapping left++, the value before left is larger than the reference point by slowly zooming out. Partitions occur because they are swapped as soon as they are found to be larger than the reference point.

Also, the conditions that determine ascending or descending order are extracted, as is the rule of sort. Compare = (a, b) => b – a.

    for(let i = left; i <= right; i++) {
        if (compare(arr[i], pivot) < 0) {
            swap(arr, i, left)
            left++
        }
    }
    
    // After the split, we know that the last left was incremented by 1 after the last swap.
    // Left-1 is the first element to the left of the datum. Let's say [4, 5, 6, 3, 2] 6 is the last swap.
    // He swaps with the reference point and puts the reference point in the right place. [6, 5, 4, 3, 2]
    swap(arr, pivotIdx, left - 1)
    // Then return the subscript of the reference point
    return left - 1
    
    // The following examples are in ascending order, when exchanged in hours over datum
    // [3, 4, 6, 2, 1]
        /* 3 i 1 index 1 -- 4 < 3 4 i 2 index 1 -- 6 < 3 6 i 3 index 1 -- 2 < 3 --> index 2 [3, 2 , 6, 4, 1 I 4 index 2 -- 1 < 3 --> index 3 [3, 2, 1, 4, 6] 1 I 5 break 2 */

        // [3,2,1,5,6,4]  2
        /* 3 i index 1 2 i 1 index 1 2 < 3 --> index--2 [3, 2, 1, 5, 6, 4] 1 i 2 index 2 1 < 3 --> index--3 [3, 2, 1, 5, 6, 4] 5 I 3 index 3 5 < 3 6 I 4 index 3 6 < 3 4 I 5 index 3 4 < 3
Copy the code

We’re going to do the recursion. So what recursion does is it’s very simple, it splits the array over and over again. In order to do subtraction in this step, first we need to know the index of the KTH element, so we pass it in as an argument to the function. It then performs a binary recursive operation depending on whether the index of the KTH element is to the left or right of the reference point.

    function quickSort(arr, k, left = 0, right = arr.length) {
        constDatum subscript = segmentation (ARR, left, right)// Select the interval to the left of the reference point or the interval to the right of the reference point
        if(base subscript < k) {quickSort(arr, k, left, base subscript -1)}else if(base subscript > k) {quickSort(arr, k, base subscript +1, right)
        }
    }
Copy the code

And in order to deal with the recursive termination condition, we need left < right, they can’t be equal, at least two elements in the interval.

Above is the quick sorting of the analysis, the complete code is as follows.

    function findKthLargest(nums, k) {
        quickSort(nums, k - 1)

        return nums[k - 1]}function quickSort(arr, k, left = 0, right = arr.length - 1) {
        if (left < right) {
          const pivotIdx = partition(arr, left, right)
          if (pivotIdx > k) {
            quickSort(arr, k, left, pivotIdx - 1)}else if (pivotIdx < k) {
            quickSort(arr, k, pivotIdx + 1, right)
          }
        }

        return arr
    }
    
    // Ascending descending judgment
    const compare = (a, b) = > b - a
    function partition(arr, left, right) {
        let pivotIdx = Math.floor(Math.random() * (right - left + 1)) + left
        const pivot = arr[pivotIdx]
        swap(arr, pivotIdx, left)
        pivotIdx = left
        left = left + 1

        for (let i = left; i <= right; i++) {
          if (compare(arr[i], pivot) < 0) {
            swap(arr, i, left)
            left++
          }
        }

        swap(arr, pivotIdx, left - 1)
        return left - 1
    }

    function swap(arr, n1, n2) {
        ;[arr[n1], arr[n2]] = [arr[n2], arr[n1]]
    }
Copy the code

The heap

Ps again: heap in anotherThe articleDetailed introduction. I don’t want to repeat it so you can skip over it.

The complete code for this question is as follows:

    function findKthLargest(nums, k) {
        const heap = new Heap()
        for(let i = 0; i < nums.length; i++) {
            // The top of the heap is the smallest element in the largest heap
            // The smallest heap is the opposite
            if(heap.size() === k && heap.data[0] > val ) {
                continue
            }
            heap.push(nums[i])
            if (heap.size() > k) {
                heap.pop()
            }
        }
        
        return heap.data[0]}class Heap {
        constructor() {
          this.data = []
          this.compare = (a, b) = > a - b
        }

        size() {
          return this.data.length
        }

        swap(n1, n2) {
          const { data } = this
          const temp = data[n1]
          data[n1] = data[n2]
          data[n2] = temp
        }

        push(val) {
          this.data.push(val)
          this.bubblingUp(this.size() - 1)}pop() {
          if (this.size() === 0) return null
          const { data } = this
          const discard = data[0]
          const newMember = data.pop()
          if (this.size() > 0) {
            data[0] = newMember
            this.bubblingDown(0)}return discard
        }

        bubblingUp(index) {
          while (index > 0) {
            const parent = (index - 1) > >1
            const { data } = this
            if (this.compare(data[index], data[parent]) < 0) {
              this.swap(parent, index)
              index = parent
            } else {
              break}}}bubblingDown(index) {
          const { data } = this
          const last = this.size() - 1
          while (true) {
            const left = index * 2 + 1
            const right = index * 2 + 2
            let parent = index
            if (left <= last && this.compare(data[left], data[parent]) < 0) {
              parent = left
            }
            if (right <= last && this.compare(data[right], data[parent]) < 0) {
              parent = right
            }
            if(index ! == parent) {this.swap(index, parent)
              index = parent
            } else {
              break}}}}Copy the code