求第k小数

这篇文章包括题目思路代码时间复杂度分析几部分。

 

题目:洛谷 求第k小的数

这里的k可以取0,也就是有第0小的数(最小的数),所以,实际上相当于平常说的求第k+1小的数

 

 

想到了几种做法,

首先,排序,输出num[k];

其次,用一个辅助数组保存前(k+1)个最小的数,遍历原数组的时候更新辅助数组(可以遍历或者大根堆),最后输出这个辅助数组中的最大值;

然后,还可以用快速排序的二分思想,将数组分成小于基准的数、基准、大于基准的数共三部分,比较k和基准的序号,相等输出基准值,小于递归基准左侧的部分,大于递归基准右侧的部分,相当于二分查找和快速排序的结合。

 

 

下面是第二种和第三种的代码

 

//方法二
#include <stdio.h>
#define MAXSIZE 5000000

long long num[MAXSIZE];//辅助数组,存储已遍历的前k+1小的数


int main()
{
    long long n, k,tmp;
    long long max = 0, max_node = 0, top = -1;

    scanf("%lld", &n);
    scanf("%lld", &k);
    for (long long i = 0; i < n; i++)
    {
        scanf("%lld", &tmp);
        if (top < k)//开始时,辅助数组元素个数小于k+1,直接加入
        {
            num[++top] = tmp;
            if (max < tmp)//记录好辅助数组的最大值和最大值的序号
            {
                max = tmp;
                max_node = top;
            }
        }
        else//当辅助数组加入了k+1个元素之后
        {
            if (max > tmp)//如果tmp小于辅助数组当前的最大值
            {
                max = tmp;
                num[max_node] = tmp;//将最大值元素替换为tmp
                for (long long i = 0; i <= top; i++)//遍历辅助数组,重新选取最大值和最大值结点
                    if (max < num[i])
                    {
                        max = num[i];
                        max_node = i;
                    }
            }
        }
    }
    printf("%lld", max);
    return 0;
}

 

//方法三
#include <stdio.h> #define MAXSIZE 5000000 long long num[MAXSIZE]; long long n, k; void sort(long long left, long long right) { if (left > right) return; long long greater = left, less = right, std = num[left]; while (greater < less) { while (less > greater&&num[less] >= std) less--; while (greater < less&&num[greater] <= std) greater++; if (greater != less) { long long tmp = num[less]; num[less] = num[greater]; num[greater] = tmp; } else { long long tmp = num[less]; num[less] = num[left]; num[left] = tmp; } } if (less == k)//原本的两部分都要递归变成了只递归一部分 printf("%lld", num[less]); else if (less > k) sort(left, less - 1); else sort(less + 1, right); } int main() { scanf("%lld", &n); scanf("%lld", &k); for (long long i = 0; i < n; i++) scanf("%lld", &num[i]); sort(0, n - 1); return 0; }

 

 

 

时间复杂度分析

 

第一种,时间复杂度为O(n*log n),太大

第二种,时间复杂度为O(n~k*n),这种方法的最好和最坏的时间复杂度相差很大,最好的情况是原数组升序排列,时间复杂度为n,最坏的情况是原数组降序排列,时间复杂度为kn,当k≈n时,更是达到n2的级别,

第三种,时间复杂度为Θ(n),写出递推方程T(n)=T(n/2)+O(n),利用主定理可以求解,而且最好和最坏时间复杂度都是Θ(n)

最后的结果是第一种60分,第二种0分、开 O2 40分,第三种100分(开O2更慢)

可能是测试样例太***钻,导致第二种成绩这么差吧,唉~

 第二种,如果把辅助数组当做大根堆,每次更新堆而不是遍历数组,那么时间复杂度是O(n*log k)

posted @ 2020-07-14 17:35  _lyl  阅读(427)  评论(0编辑  收藏  举报