zoukankan      html  css  js  c++  java
  • O(N)的时间寻找最大的K个数

    寻找N个数中最大的K个数,本质上就是寻找最大的K个数中最小的那个,也就是第K大的数。

    可以使用二分搜索的策略来寻找N个数中的第K大的数。对于一个给定的数p,可以在O(N)的时间复杂度内找出所有不小于p的数。

    寻找第k大的元素:

    #include <iostream>
    using namespace std;
    
    //快速排序的划分函数
    int partition(int a[],int l,int r)
    {
        int i,j,x,temp;
        i = l;
        j = r+1;
        x = a[l];
        //将>=x的元素换到左边区域
        //将<=x的元素换到右边区域
        while (1)
        {
            while(a[++i] > x);
            while(a[--j] < x);
            if(i >= j) break;
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        a[l] = a[j];
        a[j] = x;
        return j;
    }
    
    //随机划分函数
    int random_partition(int a[],int l,int r)
    {
        int i = l+rand()%(r-l+1);//生产随机数
        int temp = a[i];
        a[i] = a[l];
        a[l] = temp;
        return partition(a,l,r);//调用划分函数
    }
    
    //线性寻找第k大的数
    int random_select(int a[],int l,int r,int k)
    {
        int i,j;
        if (l == r) //递归结束
        {
            return a[l];
        }
        i = random_partition(a,l,r);//划分
        j = i-l+1;
        if(k == j) //递归结束,找到第K大的数
            return a[i];
        if(k < j)
        {
            return random_select(a,l,i-1,k);//递归调用,在前面部分查找第K大的数
        }
        else
            return random_select(a,i+1,r,k-j);//递归调用,在后面部分查找第K大的数
    } int main() { int a[]={1,2,3,4,6,6,7,8,10,10}; cout<<random_select(a,0,9,1)<<endl; cout<<random_select(a,0,9,5)<<endl; return 0; }

    如果所有N个数都是正整数,且它们的取值范围不太大,可以考虑申请空间,记录每个整数出现的次数,然后再从大到小取最大的K个。比如,所有整数都在(0, MAXN)区间中的话,利用一个数组count[MAXN]来记录每个整数出现的个数(count[i]表示整数i在所有整数中出现的个数)。只需要扫描一遍就可以得到count数组。然后,寻找第K大的元素:

    for(sumCount = 0, v = MAXN-1; v >= 0; v--)
    {
        sumCount += count[v];
        if(sumCount >= K)
            break;
    }
    return v;

    极端情况下,如果N个整数各不相同,我们甚至只需要一个bit来存储这个整数是否存在(bit位为1或为0),这样使用的空间可以大大压缩。

    当然也可以使用像计数排序、桶排序等这些以O(N)的时间排序算法也可以寻找第K大的数,但这也是以空间换时间为代价的。

    实际情况下,并不一定保证所有元素都是正整数,且取值范围不太大。上面的方法仍然可以推广使用。如果N个数中最大的数Vmax,最小的Vmin,我们可以把这个区间[Vmax,Vmin]分成M块,每个小区间的跨度为d=(Vmax-Vmin)/M,即[Vmin,Vmin+d],[Vmin+d,Vmin+2d]......然后,扫描一遍所有元素,统计各个小区间中的元素个数,就可以知道第K大的元素在哪一个小区间。然后,再在那个小区间中找第K大的数(此时这个小区间中,第K大的数可能就是第T大的数了,这个T和每个小区间的个数有关)。我们需要找一个尽量大的M,但M的取值受到内存的限制。

  • 相关阅读:
    Java Spring Boot VS .NetCore (十) Java Interceptor vs .NetCore Interceptor
    Java Spring Boot VS .NetCore (九) Spring Security vs .NetCore Security
    IdentityServer4 And AspNetCore.Identity Get AccessToken 问题
    Java Spring Boot VS .NetCore (八) Java 注解 vs .NetCore Attribute
    Java Spring Boot VS .NetCore (七) 配置文件
    Java Spring Boot VS .NetCore (六) UI thymeleaf vs cshtml
    Java Spring Boot VS .NetCore (五)MyBatis vs EFCore
    Java Spring Boot VS .NetCore (四)数据库操作 Spring Data JPA vs EFCore
    Java Spring Boot VS .NetCore (三)Ioc容器处理
    Java Spring Boot VS .NetCore (二)实现一个过滤器Filter
  • 原文地址:https://www.cnblogs.com/luxiaoxun/p/2624799.html
Copyright © 2011-2022 走看看