TopK问题代码(Python,Java)

主要有两种方法吧,调用函数的就不用讲了,面试可能会被打。。。主要还是两个经典的算法,一个堆排序,一个是快排的升级版本:快速选择的方法。

快速选择法

第一种是快速选择的方法,利用的是快速排序中将分区分成两个部分,那么如果左边分区长度大于K,就可以缩小范围,如果左边分区小于K,那么只要在右边的分区中找出剩余的数量就可以了。如果快排熟悉的话,其实还是挺简单的,需要注意的点就是需要随机选择。

import random
def partition(nums,left,right):
    target = nums[left]
    raw = left
    left=left+1
    while left<= right:
        while left<=right and nums[right] > target:
            right -= 1
        while left<= right and nums[left] < target:
            left += 1
        if left <= right:#这个需要注意。
            nums[left],nums[right] = nums[right],nums[left]
    nums[raw],nums[left-1] = nums[left-1],nums[raw]
    return left -1

def randomPartition(nums,left,right):
    target = random.randint(left,right)
    #随机取一个数然后交换他们的位置!!不然会出bug
    nums[target],nums[right] = nums[right],nums[target]
    pivo = partition(nums,left,right)
    return  pivo

def selectMinNum(nums,left,right,k):
    pivo = randomPartition(nums,left,right)
    if k == pivo - left + 1:
        return ;
    if k < pivo - left+1:
        selectMinNum(nums,left,pivo-1,k)
    if k > pivo - left+ 1:
        selectMinNum(nums,pivo+1,right,k-pivo+left-1)

def selectMaxNum(nums,left,right,k):
    pivo = randomPartition(nums,left,right)
    if k ==right-pivo+1:
        return;
    if k < right-pivo+1:
        selectMinNum(nums,pivo+1,right,k)
    if k > right-pivo+1:
        selectMinNum(nums,left,pivo-1,k-right+pivo-1)

这个还是挺容易的理解的,看代码的话,应该可以看懂,这个是可以直接使用的代码,核心就是在于利用快排和二分的思想,复杂度的话期望是O(N)是不是很神奇呢,详情可以参考算法导论第九章哦.

里面包括两个,求最小的N和最大的N。就是把符号换一下。

使用最大堆和最小堆的思想,每次维护一个容量为k的堆,每次进来一个数,进行堆的改造。将进来的树放在最后,然后从下到上对堆进行改造。

#这个函数是从上到下进行堆的调整
def maxHeapOne(list2,index1):
    if index1*2+1 <= len(list2)-1:
        leftChild = index1*2+1
        rightChild = index1*2+2
        if rightChild > len(list2)-1:
            if list2[index1] < list2[leftChild]:
                list2[index1],list2[leftChild] = list2[leftChild],list2[index1]
            return
        if list2[index1] > list2[rightChild] and list2[index1] > list2[leftChild]:
            return
        if list2[leftChild]> list2[rightChild]:
            if list2[index1] < list2[leftChild]:
                list2[index1],list2[leftChild] = list2[leftChild],list2[index1]
                maxHeapOne(list2,leftChild)
        if list2[leftChild] < list2[rightChild]:
            if list2[index1] < list2[rightChild]:
                list2[index1],list2[rightChild] = list2[rightChild],list2[index1]
                maxHeapOne(list2,rightChild)

#这个函数是从下到上对堆进行调整,前提是其前n-1项为最大堆
def heapUpdate(list2):
    N = len(list2)
    cur = N-1
    faNode = cur>>1
    while faNode >=0:
        if list2[faNode] > list2[cur]:
            return
        if list2[faNode] < list2[cur]:
            list2[faNode],list2[cur] = list2[cur],list2[faNode]
        cur = faNode
        faNode = (faNode-1)>>1


#构建堆
def createHeap(list2):
    N = len(list2)
    # cur = N-1
    start = (N-1)>>1
    while start >= 0:#对最后一个子节点的父节点开始遍历。对每一个节点都构建最大堆。
        # print(start)
        maxHeapOne(list2,start)
        start -= 1
#获取最大值
def getMax(list2):
    createHeap(list2)
    list2[0],list2[-1] = list2[-1],list2[0]
    res = list2.pop()
    maxHeapOne(list2,0)   #取出最大值后对堆进行调整,从上到下进行调整
    return res

#minTOPN

def getminTop(list2,k):
    list3 = list2[:k]#先建立一个k的最大堆
    createHeap(list3) 
    for i in list2[k:]: # 对后面的元素以此插入,调整堆
        list3 +=[i]
        heapUpdate(list3)
        list3 = list3[-k:]
    return list3

最重要的是前两个函数,一个是从上到下对堆进行改造,第二个是从下到上对堆进行改造。其余的函数也是比较容易理解的,当然对于最后一个函数getminTop 这个函数,优化的点还是很多的,我就懒得写了。。。大家自己去优化吧。

原文地址:https://www.cnblogs.com/tjpeng/p/12534804.html