利用类快速排序求数组中第k大的值

如果要求出一个数组中第k大的值可以采用以下策略:

  • 排序法:利用各种排序算法对数组排序后,再找到第k大的数。利用计数排序可以将时间复杂度压缩到O(N)
  • 部分排序法,利用冒泡、选择排序,或者堆排序,依次找第1大、第2大、…第k大的值
  • 类快速排序方法

我们以一个例子来理解类快排的方式:
比如找输入数组arr [4, 5, 7, 6, 3, 1, 2, 10, 9],K 为 4,即找数组中第4大的数

我们先以数组arr[0],也就是4为基准,利用快排的原理,将比4小的放左边,比4大的放右边。利用类快速排序求数组中第k大的值_第1张图片
因为右边有5个数字比4大,此时4肯定是第6大的数字。并且可以知道第4大的数字肯定在右边。

接着对右边进行相同的操作,右边第一个元素6作为基准值,小的放左边,大的放右边。
利用类快速排序求数组中第k大的值_第2张图片
此时6的右边有3个数比6大,就说嘛6就是第4大的元素。找到了第4大的元素,结束程序。

代码实现

function findKthOfN (arr, k) {
     
  function findKthOfNByQuickSort (arr, low, high, k) {
     
    var baseIndex = low, leftIndex = low, rightIndex = high, splitIndex
    while (1) {
     
      while (arr[leftIndex] <= arr[baseIndex]) {
     
        leftIndex ++
      }
      while (arr[rightIndex] >= arr[baseIndex]) {
     
        rightIndex --
      }
      
      if (leftIndex >= rightIndex) {
     
        leftIndex = leftIndex > high ? high : leftIndex
        rightIndex = rightIndex < low ? low : rightIndex
        splitIndex = arr[leftIndex] < arr[rightIndex] ? leftIndex : rightIndex
        break
      }
      
      
      var tmp = arr[leftIndex]
      arr[leftIndex] = arr[rightIndex]
      arr[rightIndex] = tmp

    }
    tmp = arr[splitIndex]
    arr[splitIndex] = arr[baseIndex]
    arr[baseIndex] = tmp
    // 完成快排的分大小值
    if (arr.length - splitIndex === k) return arr[splitIndex]
    if (arr.length - splitIndex > k) return findKthOfNByQuickSort(arr, splitIndex+1, high, k)
    if (arr.length - splitIndex < k) return findKthOfNByQuickSort(arr, low, splitIndex-1, k)
  }
  return findKthOfNByQuickSort(arr, 0, arr.length-1, k)
}

将上诉代码改一个地方就变成了,求某个数组最大的K个元素,但是求得的最大K个元素是无序的。

function findTopK (arr, k) {
     
// ...
if (arr.length - splitIndex === k) return arr.slice(splitIndex)
// ...
}

运行结果如下:

var arr = [4, 5, 7, 6, 3, 1, 2, 10, 9]
console.log(findKthOfN(arr, 4)) // 6
console.log(findTopK(arr, 4)) // [ 6, 7, 10, 9 ]

扩展

在不排序的情况下求数组的中位数。
相当于K = 2/N,也属于求数组中第K大的数。

function getMid (arr) {
     
  var mid, len = arr.length
  if (len % 2 === 0) {
     
    mid = findKthOfN (arr, len/2)
    mid += findKthOfN (arr, len/2 + 1)
    mid /= 2
  } else {
     
    mid = findKthOfN (arr, Math.ceil(len/2))
  }
  return mid
}

你可能感兴趣的:(利用类快速排序求数组中第k大的值)