寻找最大的k个数

简述

给定n个数,从中找到最大的k个数

思路

  • 思路1:最简单的想法,把给定的n个数排序一遍,再取前k个数,复杂度是O(n*logn) + O(k),这里排序用的是快排,顺手下了一个,代码如下
void quickSort(int* array, int begin, int end)
{
    int first = begin;
    int last = end;
    int key = array[first];
    //terminate
    if (first >= last)
        return;
    //swap
    while (first < last)
    {
        while (first < last && array[last] <= key)
            last--;
        array[first] = array[last];
        while (first < last && array[first] >= key)
            first++;
        array[last] = array[first];
    }
    array[first] = key;
    //recursion
    quickSort(array, begin, first - 1);
    quickSort(array, last + 1, end);
}
  • 思路2:如果k很小,比如k为1时,只需要找到最大的数,那么进行n-1次比较就行了,用思路1的方法显然浪费时间,所以这个给出了一个O(n*k)的算法,在k << logn的时候,这个算法比思路1要快的多。大概思路如下:找到n个数中前k个数,放入一个数组kbig中,并找到kbig中最小的数的下标。然后对n进行遍历,每次遍历比较当前n下标的数和kbig中最小的数哪个大,若n当前下标的那个数大,则把kbig中最小的数赋值为n当前下标的数,然后再对kbig中做一次遍历,找到新的kbig中最小的数。(也可以不用开一个新的数组,直接在n上做交换,这样做的效果就是对前k大的数做了排序,放到前k个位置上)时间复杂度是O(n * k),函数代码如下:
void selectK(int* array, int* kBig, const int numConst, int k)
{
    //one condition
    if (k >= numConst)
    {
        for (int i = 0; i < numConst; i++)
        {
            kBig[i] = array[i];
        }
    }
    //another condition
    else
    {
        int minIndex;
        for (int i = 0; i < numConst; i++)
        {
            if(i < k)
            {
                kBig[i] = array[i];
                continue;
            }
            if (i == k)
            {
                minIndex = 0;
                for(int j = 1; j < k; j++)
                {
                    if(kBig[j] < kBig[minIndex])
                    {
                        minIndex = j;
                    }
                }
            }
            if (array[i] > kBig[minIndex])
            {
                kBig[minIndex]  = array[i];
                minIndex = 0;
                for(int j = 1; j < k; j++)
                {
                    if(kBig[j] < kBig[minIndex])
                    {
                        minIndex = j;
                    }
                }
            }
        }
    }
}
  • 思路3:与思路2相同,但是在找当前kbig中最小数的时候做了优化,通过维护一个最小堆,来加速查找和插入的过程,时间复杂度是O(n*logk),代码如下,由于这里查找的都是正数,所以投篮了,一开始把堆中元素都设为-1:
void kBigHeap(int* array, int* kBig, const int numConst, int k)
{
    //one condition
    if (k >= numConst)
    {
        for (int i = 0; i < numConst; i++)
        {
            kBig[i] = array[i];
        }
    }
    else
    {
        for (int i = 0; i < k; i++)
            kBig[i] = -1;
        for (int i = 0; i < numConst; i++)
        {
            if(array[i] > kBig[0])
            {
                kBig[0] = array[i];
                int p = 0;
                int q;
                while(p < k)
                {
                    q = 2 * p + 1;
                    if(q >= k)
                        break;
                    if(q < k - 1 && kBig[q + 1] < kBig[q])
                        q++;
                    if(kBig[q] < kBig[p])
                    {
                        int tmp = kBig[q];
                        kBig[q] = kBig[p];
                        kBig[p] = tmp;
                        p = q;
                    }
                    else
                        break;
                }
            }
        }
    }
}
  • 思路4:采用分治的办法,把一个复杂的问题分成两个规模小的问题来解决。在n数组中随即找到一个元素X,把数据分成sa和sb,sa中元素大于等于X,sb中元素小于X,这时有两种可能性:(1)sa中元素的个数小于k,那么sa中所有的数和sb中最大的k-|sa|个元素就是数组n中最大的k个数
    (2)sa中元素个数大于等于k,则需要返回sa中最大的k个元素
    给出核心函数代码如下,用Python写的,时间复杂度也是O(n*logk):
import random
def kBig(s, k):
    if k <= 0:
        return []
    if (len(s) <= k):
        return s
    sa, sb = partition(s)
    ka = kBig(sa, k)
    kb = kBig(sb, k - len(sa))
    ka.extend(kb)
    return ka

def partition(s):
    sa = []
    sb = []
    tmp = len(s) * random.random() * 0.999
    stmp = s[0]
    s[0] = s[int(tmp)]
    s[int(tmp)] = stmp

    p = s[0]
    for i in s[1:]:
        if i > p:
            sa.append(i)
        else:
            sb.append(i)
    if len(sa) < len(sb):
        sa.append(p)
    else:
        sb.append(p)

    return sa,sb
  • 思路5:如果N个数都是正整数,且有取值范围,可以申请一个空间,如取值范围是1~MAX,可以开一个数组count[MAX],其中count[i]代表i+1出现的次数,遍历一次就可以得到所有整数出现的次数,然后寻找第K大的数(找到第K大的代表比这个数大的都是前K个最大的数,再遍历一次即可)

  • 思路6:如果不是正整数的情况,可以划分区间,如N个数中最大的是Vmax,最小的是Vmin,划分成M块,则每块区域是d = (Vmax - Vmin)/ M,第i块区域为[Vmin + (i - 1) × d, Vmin + i × d],然后用思路5的方法,等找到第K大的数所在的区域,再遍历一次,把那个区间的数都放到一个数组中排序,就能找到,所以M越大,需要空间就越大,但是找数的时候就会减少时间复杂度,但是M越小,找数的时候就比较复杂,如果M = 1,相当于对N个数进行排序,那么就是O(N×logN),需要去一个平衡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值