快排时间复杂度_TopK问题(基于堆排和快排的实现)

Top K 问题相信大家在面试过程中,经常被问到,下面就和大家一起探讨下两种常见的算法实现。

一. 基于堆排序实现

思路
  基于数组的前K个数,生成一个小顶堆,数组剩余元素依次与堆顶数据比较,小于等于堆顶数据时直接舍弃,大于堆顶数据时替换掉堆顶数据,并调整堆结构保证满足小顶堆要求。算法优势
  时间复杂度是O(N*logK),不需要将数组一次全部加载到内存中,可以处理海量数据。图解
4f8e15dfe2973c0ce74157b0902ce3b0.png

TopK-堆排序法

  • 代码实现(Golang)
// TopK-堆排序法func TopKByMinHeap(nums []int, k int) []int {    length := len(nums)    // 数组长度小于k,直接返回    if length < k {             return nums    }            // 取出数组前k个数据,并生成小顶堆    minHeap := make([]int, 0)    minHeap = append(minHeap, nums[:k]...)      // 前k个数据生成小顶堆    CreateMinHeap(minHeap)    // 遍历数组剩余数据,大于堆顶数据时,替换堆顶,重新维护小顶堆    for i := k; i < length; i++ {        if nums[i] > minHeap[0] {            minHeap[0] = nums[i]            MinHeapify(minHeap, 0, k)        }    }    return minHeap}// 自底向上创建小顶堆func CreateMinHeap(nums []int) {    length := len(nums)    for i := length - 1; i >= 0; i-- {        MinHeapify(nums, i, length)    }}// 维护小顶堆func MinHeapify(nums []int, posIndex, length int) {    // 堆左孩子节点index    leftIndex := 2*posIndex + 1    // 堆右孩子节点index    rightIndex := 2*posIndex + 2    // 当前节点以及左右孩子节点中最小值的index, 初始化为当前节点index    minIndex := posIndex    // 左孩子存在并且小于当前节点值时, 最小值index替换为左孩子index    if leftIndex < length && nums[leftIndex] < nums[minIndex] {        minIndex = leftIndex    }    // 右孩子存在并且小于当前节点值时, 最小值index替换为右孩子index    if rightIndex < length && nums[rightIndex] < nums[minIndex] {        minIndex = rightIndex    }    // 最小值节点index不等于当前节点时,替换当前节点和其中较小孩子节点值    if minIndex != posIndex {        nums[posIndex], nums[minIndex] = nums[minIndex], nums[posIndex]        MinHeapify(nums, minIndex, length)    }}

二. 基于快排实现

  • 思路
      利用快排的分划函数找到分界点位置K,则前K个数据即所求结果。
  • 算法优势
      时间复杂度是O(N),对于可以一次性加载到内存的数组效率很高。
  • 图解
cdd48a615c0fae1c842aac2665877e4d.png

TopK-快速排序法

  • 代码实现(Golang)
// TopK-快速排序法func TopKByQuickSort(nums []int, k int) []int {    length := len(nums)    // 数组长度小于k,直接返回    if length < k {        return nums    }    // 数组进行快排,左侧边界    left := 0    // 数组进行快排,右侧边界    right := length    // 第一次快排后,获取分界点index    pivotIndex := partition(nums, left, right)        // 循环快排,找到分界点index刚好等于k    for pivotIndex != k {        if pivotIndex < k {            // 分界点index小于k,继续对分界点右侧进行快排,重新获取分界点index            left = pivotIndex + 1        } else {            // 分界点index大于k,缩小快排范围为上次分界点与本次分界点之间数据,重新获取分界点index            right = pivotIndex        }        pivotIndex = partition(nums, left, right)    }    // 取前k个有序数据    return nums[:k]}// 按分界点,进行快排,并返回分界点indexfunc partition(nums []int, left, right int) int {    // 初始化分界值为左边界值    pivot := nums[left]    // 所有大于分界值的数据边界index    pos := left        // 小于分界值时,边界扩展,将数据替换到边界值index位置,    for i := left; i < right; i++ {        if nums[i] > pivot {            pos++            nums[i], nums[pos] = nums[pos], nums[i]        }    }        // 交换分界值的数据边界index和分界点index,使得分界点左侧均大于分界点,右侧均小于分界点    nums[pos], nums[left] = nums[left], nums[pos]    return pos}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值