【编程之美】2.5 寻找最大的k个数

有若干个互不相等的无序的数,怎么选出其中最大的k个数。

我自己的方案:因为学过找第k大数的O(N)算法,所以第一反应就是找第K大的数。然后把所有大于等于第k大的数取出来。

写这个知道算法的代码都花了2个多小时,反思,太慢了。 注意边界条件,不要混混沌沌的。

/************我自己的解法*****************/
//选择数组a[N]中最大的第k个数
int Select(int * a, int N, int k)
{
    if(k > N || a == NULL)
    {
        cout << "error!!!";
    }
    if(N < 5)
    {
        qsort(a, N, sizeof(a[0]), cmp);
        return a[N - k];
    }
    int arrNum = N/5; //可以整除5的数字
    int surplus = N % 5; //除以5剩下的数字
    int * mid = new int [arrNum]; //存储每一组的中位数
    int * S1 = new int [N];
    int * S2 = new int [N];
    int posS1 = 0;
    int posS2 = 0;

    for(int i = 0; i < arrNum; i++)
    {
        qsort(a+i*5, 5, sizeof(a[0]), cmp);
        mid[i] = *(a + i * 5 + 3);
    }

    int midNum = Select(mid, arrNum, arrNum/2);

    for(int i = 0; i < arrNum; i++)
    {
        int * currentGroup = a + i * 5;
        if(currentGroup[2] < midNum)
        {
            memcpy(S1 + posS1, currentGroup, 3 * sizeof(a[0]));
            posS1 += 3;
            for(int j = 3; j < 5; j++)
            {
                if(currentGroup[j] <= midNum)
                    S1[posS1++] = currentGroup[j];
                else
                    S2[posS2++] = currentGroup[j];
            }
        }
        else
        {
            memcpy(S2 + posS2, currentGroup + 2, 2 * sizeof(a[0]));
            posS2 += 3;
            for(int j = 0; j < 2; j++)
            {
                if(currentGroup[j] <= midNum)
                    S1[posS1++] = currentGroup[j];
                else
                    S2[posS2++] = currentGroup[j];
            }
        }

    }
    for(int i = arrNum * 5; i < N; i++)
    {
        if(a[i] <= midNum)
            S1[posS1++] = a[i];
        else
            S2[posS2++] = a[i];
    }

    if(k == posS2)
    {
        return midNum;
    }
    else if(k < posS2)
    {
        return Select(S2, posS2, k);
    }
    else
    {
        return Select(S1, posS1, k - posS2);
    }

}

int * GetBiggestK(int * a, int N, int k)
{
    int * biggestK = new int [k];
    int numK = Select(a, N, k);
    int pos = 0;
    for(int i = 0; i < N; i++)
    {
        if(a[i]>=numK)
        {
            biggestK[pos++] = a[i];
        }
    }

    return biggestK;

}

编程之美对这种算法的评价是:由于这个线性算法的常数项比较大,在实际应用中有时效果并不好。

---------------------------------------------------------------------------------------------------------------------------

书上的解法:

解法一: 先排序,再取出最大的k个数 复杂度 O(NlogN)

解法二:类似于快速排序,先在数据中选一个数做标准,把数据划分为两个部分,大于等于选中数字S1个 和 小于选中数字S2个 。如果S1大于k则在S1个数中继续找最大的k个,如果S1小于k,则把这S1个数加入答案,并在S2中继续找其他的数字。 平均复杂度为O(NlogK).

/*********答案中 解法二*****************/
int * GetBiggestKAns2(int * a, int N, int k)
{
    if(a == NULL || k > N)
    {
        cout << "error!!!"<< endl;
        return NULL;
    }
    int first = a[0];
    int head = 1;
    int tail = N - 1;
    int numAlready = 0;

    int postion = GetPosition(a, first, head, tail);
    swap(a[0], a[postion]);

    if(postion + 1 == k)
    {
        int * ans = new int [k];
        memcpy(ans, a, k * sizeof(a[0]));
        return ans;
    }
    else if(postion + 1 > k)
    {
        return GetBiggestKAns2(a, postion + 1, k);
    }
    else
    {
        int * ans = new int [k];
        memcpy(ans, a , (postion + 1) * sizeof(a[0]));
        int * ansPart = GetBiggestKAns2(a + postion + 1, N - postion - 1, k - postion - 1);
        memcpy(ans + postion + 1, ansPart, (k - postion - 1) * sizeof(a[0]));
        return ans;
    }
}

//交换时让大数在前,小数在后
int GetPosition(int * a, int first, int p, int r)
{
    int head = p;
    int tail = r;
    while(head < tail)
    {
        while(a[head] >= first)
            head++;
        while(a[tail] < first)
            tail--;
        if(head < tail)
        {
            swap(a[head], a[tail]);
            head++;
            tail--;
        }
    }
    return head - 1; //如果所有的数字都比first大 那么head会指向数组后的第一个数字
                     //如果所有的数字都比first小 那么head会指向第一个数字 都是恰好head - 1 是应该交换的位置
}
时间: 2024-10-07 18:41:07

【编程之美】2.5 寻找最大的k个数的相关文章

编程之美2.5 寻找最大的K个数

在一个数组中寻找最大的K个数,我们首先说一种非常简单的方法,利用快速排序中的分割算法,即我们经常看见的partition.这个函数会返回一个 int 类型的值,这个值代表的是前一半数字和后一半数字的分割点,前一半数字都小于等于后一半数字(递增排序),所以,我们只要找到相对应的分割点,即可以找到最大的K个数,或者最小的K个数,这就是利用线性方法可以完成任务的方法. 首先,给出函数声明: int DutPartition(int*, int, int); void DutFindMaxKInArra

编程之美之2.5 寻找最大的K个数

[题目] 有很多无序的数,从中找出最大的K个数.假定他们都不相等. [解法一] 如果数据不是很多,例如在几千个左右,我们可以排一下序,从中找出最大的K个数.排序可以选择快速排序或者堆排序 [cpp] view plaincopy #include<stdio.h> #include<stdlib.h> int cmp(const void *a,const void *b){ return *(int *)a - *(int *)b; } int main(){ int n,k;

编程之美之寻找最大的k个数

这个题目很常见,方法也很多,这里总结了编程之美给出的几个比较好的方法,也算是对这个问题的一个总结. 方法一.partition算法,每次partition的复杂度为O(n),总的平均时间复杂度为O(nlogn) 分析:运用partition算法,如果返回的provit > k-1,则说明要找的数都在前面,把end= provit-1;如果provit < k-1,表明找到一部分,先把部分数据保存,然后start = provit + 1;如果正好相等,则保存后结束,具体代码如下: int pa

寻找最小的K个数

寻找最小的K个数 题目描述:查找最小的K个数 题目:输入n个整数,输出其中最小的K个数 例如,输入1.2.3.4.5.6.7.8这8个数字,则最小的4个数字为1.2.3.4. 第一节.各种思路,各种选择 要求一个序列中最小的K个数,按照惯有的思维方式,很简单,先对这个序列从小到大排序,然后输出前面的最小的K个数即可: 至于选取什么样的排序方法,第一时间应该想到的是快速排序,我们知道,快速排序平均时间复杂度为O(nlogn),然后再遍历序列中前K个元素输出,即可,总的时间复杂度为O(nlogn +

寻找最大的K个数

编程之美有一道考察多种排序的题目,题目如下: 有一个长度为N的无序数组,假定其中的每一个元素都各不相等,求其中最大的K个数. 作者对于此题目结合各种排序算法给出了五种解法思路. 解法一: 使用快速排序或堆排序对它们元素进行排序,整个排序的时间复杂度为O(N*log2N),然后取出前K个,时间复杂度为O(K),总时间复杂度O(N*log2N)+O(K)=O(N*log2N).根据题目要求,并不需要后N-KN-K个数有序,而只需要寻找最大K的个数,因此可以用选择排序和交换排序进行部分排序来筛选最大的

寻找最小的k个数(update)

类似编程之美中寻找最大的k个数 解法一: 题目没有要求最小的k个数有序,也没要求最后n-k个数有序.既然如此,就没有必要对所有元素进行排序.这时,咱们想到了用选择或交换排序,即: 1.遍历n个数,把最先遍历到的k个数存入到大小为k的数组中,假设它们即是最小的k个数: 2.对这k个数,利用选择或交换排序找到这k个元素中的最大值kmax(找最大值需要遍历这k个数,时间复杂度为 O(k) ): 3.继续遍历剩余n-k个数.假设每一次遍历到的新的元素的值为x,把x与kmax比较:如果 x < kmax

寻找最大的K个数(下)

接着昨天的写,里面的代码包含昨天的 1 #include <iostream> 2 using namespace std; 3 #define N 50 4 5 //初始化数组 6 int a[] = {6, 2, 3, 4, 4, 3, 1, 2, 4, 4}; 7 //int a[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; 8 //int a[] = {1, 2, 3, 4, 5, 6}; 9 //int a[] = {6, 2, 3, 9, 4, 10,

编程之美之高速寻找多个数满足给定条件

一.寻找三个数之和等于给定值 分析:方法类似与2Sum.就是先对数组进行排序,时间复杂度为O(nlogn),然后固定一个数,用两个指针进行遍历,找到三个数之和等于给定的值就可以,时间复杂度为O(n^2).详细可提交于leetcode:https://oj.leetcode.com/problems/3sum/,代码例如以下: vector<vector<int> > threeSum(vector<int> &num,int target){ vector<

编程之美 2.10寻找数组中的最大最小值

数组是最简单的一种线性数据结构,当得到一个数组,需要找出最大最小值的时候,通过什么样的方法可以高效的,找出最大最小值呢.对于一个N个整数组成的数组,需要比较多少次呢. 现在有一个N=8的数组{5,6,8,3,7,9,1,2}. 解法一: 将找最大和最小数看成2个独立的问题,分别求解,需要遍历数组2次,共需要2N次操作. 1 #include "iostream" 2 using namespace std; 3 void Search_max_and_min(int* a,int N)