面试之巧解TopK

正文

问题描述:

从arr[1, n]这n个数中,找出最大的k个数,这就是经典的TopK问题。

栗子:

从arr[1, 12]={5,3,7,1,8,2,9,4,7,2,6,6} 这n=12个数中,找出最大的k=5个

方法一:k次冒泡排序

我们知道一次冒泡排序可以将一个数‘冒’到目的位置
那么我们只要调用K次冒泡排序即可

方法二:堆排序

朴实无华的手写堆排

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        #建立最大堆 这个堆的大小是k [0,k-1]
        def heapfy(heap,length,i):
            maxIndex=i
            left=2*i+1
            right=2*i+2
            if left<length and heap[maxIndex]<heap[left]:
                maxIndex=left
            if right<length and heap[maxIndex]<heap[right]:
                maxIndex=right
            if maxIndex!=i:
                heap[i],heap[maxIndex]=heap[maxIndex],heap[i]
                heapfy(heap,length,maxIndex)
        if k==0:return []
        #前k个
        heap=arr[:k]
        for i in range(k//2-1,-1,-1):
            heapfy(heap,k,i)
        #后续
        for i in range(k,len(arr)):
            if arr[i]<heap[0]:
                heap[0]=arr[i]
                heapfy(heap,k,0)
        #用建好的堆进行堆排序
        for i in range(k-1,-1,-1):
            heap[0],heap[i]=heap[i],heap[0]
            heapfy(heap,i,0)
        return heap

方法三:减治法+快排partition

在学习快排的时候,调用一次partition能够将一个数(pivot)安置到最终的位置
那么如果我们用一个第k的数作为pivot,就只要调用一次partition就能得到最终答案!!
前半段就是TopK,后半段不需要管

所以我们是思路是用减治法找到第k大数的下标K_th,
然后用partition处理一遍,即可得到答案

调用减治法找Kth的时间是O(n),调用partition的时间是O(n)
空间是O(1)
完美!

微信截图_20200501215706.png


下附 Python和Java的代码
Java代码

class Solution {
    public int partition(int[] arr,int left,int right){
        //首先O(n)扫描
        int pivot=arr[left];
        int location=left;
        for(int i=left+1;i<=right;i++){
            //加不加等号都可以
            if (arr[i]<=pivot){
                swap(arr,++location,i);
            }
        }
        swap(arr,location,left);
        return location;
    }
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k==0)
            return new int[0];
        return quickSearch(arr,0,arr.length-1,k-1);

    }
    public int[] quickSearch(int[]arr,int left,int right,int k){
        int index=partition(arr,left,right);
        if (index==k)
            return Arrays.copyOf(arr, k+1);
        else if (index > k)
            return quickSearch(arr,left,index-1,k);
        else
            return quickSearch(arr,index+1,right,k);
    }
    public void swap(int[] arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
        return ;
    }
}

Python代码

#用快排的思路找到第k小的数即可
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        def partition(arr,left,right):
            pivot=arr[left]
            location=left #location指向小于等于pivot的数组段的尾部
            for i in range(left+1,right+1): 
                if arr[i]<pivot:
                    location+=1
                    arr[location],arr[i]=arr[i],arr[location]
            arr[left],arr[location]=arr[location],arr[left]
            return location #返回的是下标

        #目的是找第k大的下标
        def quickSearch(arr,left,right,k):
            index=partition(arr,left,right)
            if index==k:
                return arr[:k+1]
            if index>k:
                return quickSearch(arr,left,index-1,k)
            else:
                return quickSearch(arr,index+1,right,k)
        

        return [] if k==0 else quickSearch(arr,0,len(arr)-1,k-1)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值