转载请注明出处:http://blog.csdn.net/ns_code/article/details/26966159

题目描写叙述:

输入n个整数,找出当中最小的K个数。比如输入4,5,1,6,2,7,3,8这8个数字。则最小的4个数字是1,2,3,4。

输入:

每一个測试案例包含2行:

第一行为2个整数n,k(1<=n,k<=200000),表示数组的长度。

第二行包含n个整数。表示这n个数,数组中的数的范围是[0,1000 000 000]。

输出:

相应每一个測试案例,输出最小的k个数。并按从小到大顺序打印。

例子输入:
8 4
4 5 1 6 2 7 3 8
例子输出:
1 2 3 4

思路:

1、最直观的思路依旧是对数组进行高速排序。而后取出前k个元素。这样的时间复杂度为O(nlogn)

2、这里能够採用相似于上面那道题目的基于Partition的方法,仅仅是这次要求的分界点不是中位数,而是第k小的数,即排序后应该位于数组的第k-1个位置上的元素,这样该分界点前面的k个元素(包含该分界点)便是最小的k个数(这k个数字不一定是排序的)。跟上面那道题目分析的一样,这样的方法的平均时间复杂度为O(n),最坏情况下的时间复杂度为O(n*n),一样也能够用算法导论上提出的切割数组的方法。将最坏情况下的时间复杂度控制到O(n)。

代码例如以下:

#include<stdio.h>
#include<stdlib.h>
#include<time.h> void Swap(int *a,int *b)
{
if(*a != *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
} } /*
算法导论版快排的Partition函数
*/
int Partition(int *A,int low,int high)
{
if(A==NULL || low<0 || high<0 || low>=high)
return -1; int small = low-1;
int j;
for(j=low;j<high;j++)
{
if(A[j] <= A[high])
{
++small;
if(j != small)
Swap(&A[j],&A[small]);
}
}
++small;
Swap(&A[small],&A[high]);
return small;
} int Random_Partition(int *A,int low,int high)
{
//设置随机种子
srand((unsigned)time(0));
int index = low + rand()%(high-low+1);
Swap(&A[index],&A[high]);
return Partition(A,low,high);
} /*
返回数组A中出现次数超过一半的数字
基于Partition函数的实现
*/
void MinKNum(int *A,int len,int k)
{
if(A==NULL || len<1)
return; int low = 0;
int high = len-1;
int index = Random_Partition(A,low,high);
while(index != k-1)
{
if(index > k-1)
index = Random_Partition(A,low,index-1);
else
index = Random_Partition(A,index+1,high);
}
} int main()
{
int n,k;
while(scanf("%d %d",&n,&k) != EOF)
{
int *A = (int *)malloc(sizeof(int)*n);
if(A == NULL)
exit(EXIT_FAILURE); int i;
for(i=0;i<n;i++)
scanf("%d",A+i); MinKNum(A,n,k);
for(i=0;i<k;i++)
{
printf("%d ",A[i]);
}
printf("\n");
}
return 0;
}

3、能够考虑採用小顶堆,将数组的n个元素建成一个小顶堆,这样最小的元素就位于堆顶,将它与数组的最后一个元素交换。这样最小的元素就保存在了数组的最后一个位置。而后相同利用堆排序的思想。调整前面的n-1个元素,使之再次构成一个小顶堆,这样k次调整后,最小的k个元素便保存在了数组的最后k个位置,并且是从右向左依次增大。

这样的方法。建立小顶堆须要O(n)的时间,而后筛选出k个最小的数须要对堆调整k次。每次调整所需时间依次为O(logn)、O(log(n-1))、O(log(n-2))...O(log(n-k))。能够近似觉得每次调整须要的时间为O(logn)。这样,该方法的时间复杂度为O(n+klogn),至于空间复杂度。假设能够改变输入的数组,我们能够直接在数组上建堆和调整堆。这是空间复杂度为O(1)。假设不能改变输入数组的话,我们就要建立一个小顶堆。这样空间复杂度为O(n)。

我在九度OJ上採用的这样的方法run,结果AC,代码例如以下:

#include<stdio.h>
#include<stdlib.h> /*
arr[start+1...end]满足小顶堆的定义,
将arr[start]增加到小顶堆arr[start+1...end]中,
调整arr[start]的位置,使arr[start...end]也成为小顶堆
注:因为数组从0開始计算序号,也就是二叉堆的根节点序号为0,
因此序号为i的左右子节点的序号分别为2i+1和2i+2
*/
void HeapAdjustDown(int *arr,int start,int end)
{
int temp = arr[start]; //保存当前节点
int i = 2*start+1; //该节点的左孩子在数组中的位置序号
while(i<=end)
{
//找出左右孩子中最小的那个
if(i+1<=end && arr[i+1]<arr[i])
i++;
//假设符合堆的定义,则不用调整位置
if(arr[i]>=temp)
break;
//最小的子节点向上移动,替换掉其父节点
arr[start] = arr[i];
start = i;
i = 2*start+1;
}
arr[start] = temp;
} /*
得到最小的k个数,保存在arr中的最后面k个位置
*/
void MinHeapKNum(int *arr,int len,int k)
{
if(arr==NULL || len<1 || k<1 || k>len)
return; int i;
//把数组建成为小顶堆
//第一个非叶子节点的位置序号为(len-1)/2
for(i=(len-1)/2;i>=0;i--)
HeapAdjustDown(arr,i,len-1);
//进行堆排序
for(i=len-1;i>=len-k;i--)
{
//堆顶元素和最后一个元素交换位置。
//这样最后的一个位置保存的是最小的数,
//每次循环依次将次小的数值在放进其前面一个位置。
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//将arr[0...i-1]又一次调整为小顶堆
HeapAdjustDown(arr,0,i-1);
}
} int main()
{
int n,k;
while(scanf("%d %d",&n,&k) != EOF)
{
int *A = (int *)malloc(sizeof(int)*n);
if(A == NULL)
exit(EXIT_FAILURE); int i;
for(i=0;i<n;i++)
scanf("%d",A+i); MinHeapKNum(A,n,k);
for(i=n-1;i>=n-k;i--)
{
//依据要求的格式输出
if(i == n-k)
printf("%d\n",A[i]);
else
printf("%d ",A[i]);
}
}
return 0;
}
/**************************************************************
    Problem: 1371
    User: mmc_maodun
    Language: C
    Result: Accepted
    Time:840 ms
    Memory:8752 kb
****************************************************************/

4、还能够考虑採用大顶堆。但不是用数组的n个元素来建堆。而是用前k个数字来建立大顶堆。而后拿后面的后面的n-k个元素依次与大顶堆中的最大值(即堆顶)元素比較。假设小于该最大元素,则用该元素替换掉堆顶元素。并调整堆使其维持大顶堆的结构,假设大于该最大元素,则直接跳过,继续拿下一个数字与堆顶元素比較。等到全部的元素比較并操作完,这时数组中后面的元素都比该大顶堆中的数字要大。那么该大顶堆中的k各数字变为数组中最小的k个数字,且堆顶元素为这k个最小数组中最大的,因此它又是数组中第k小的数字。

该算法建立大顶堆须要的时间为O(k),每次调整堆须要的时间为O(logk)。而总共要调整n-k次。因此时间复杂度为

O(k+(n-k)logk),当k远远小于n时,时间复杂度可近似为O(nlogk)。另外,该算法非常适合海量数据处理,尤其在内存有限。不能一次读入全部的数据时。当n非常大,而k较小时,一次向内存读入k个数据。而后每次能够读入一个进行比較。这对于内存最多可容纳k个数据时便可满足要求。

5、也能够用数组保存k个数(事实上能够抽象为一个容器,容器选择的不同,对所需时间会有不同的影响),求其最大值。分别与后面的元素比較,利用与第4中方法相似的策略,最后该数组中个保存的便是最小的k个数字。这样的方法的时间复杂度为O(n*k)。

以上两种思路代码不再给出。


版权声明:本文博主原创文章,博客,未经同意不得转载。

【剑指offer】最小的k的数量的更多相关文章

  1. 剑指offer 最小的k个数 、 leetcode 215. Kth Largest Element in an Array 、295. Find Median from Data Stream(剑指 数据流中位数)

    注意multiset的一个bug: multiset带一个参数的erase函数原型有两种.一是传递一个元素值,如上面例子代码中,这时候删除的是集合中所有值等于输入值的元素,并且返回删除的元素个数:另外 ...

  2. 剑指Offer——最小的K个数

    题目描述: 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4. 分析: 建一个K大小的大根堆,存储最小的k个数字. 先将K个数进堆 ...

  3. python剑指offer最小的K个数

    题目描述: 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 思路: 使用快排中的partition思想. ①我们设定part ...

  4. 用js刷剑指offer(最小的K个数)

    题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 牛客网链接 js代码 function GetLeastNumbe ...

  5. 剑指 Offer——最小的 K 个数

    1. 题目 2. 解答 2.1. 方法一--大顶堆 参考 堆和堆排序 以及 堆的应用,我们将数组的前 K 个位置当作一个大顶堆. 首先建堆,也即对堆中 [0, (K-2)/2] 的节点从上往下进行堆化 ...

  6. 剑指offer--10.最小的K个数

    边界判断,坑了一下 ----------------------------------------------- 时间限制:1秒 空间限制:32768K 热度指数:375643 本题知识点: 数组 ...

  7. 剑指Offer-29.最小的K个数(C++/Java)

    题目: 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 分析: 最先想到的是将数组升序排列,返回前k个元素.不过排序的话效率 ...

  8. 剑指:最小的k个数

    题目描述 输入 n 个整数,找出其中最小的 K 个数.例如输入 4,5,1,6,2,7,3,8 这 8 个数字,则最小的 4 个数字是 1,2,3,4. 解法 解法一 利用快排中的 partition ...

  9. 2-剑指offer: 最小的K个数

    题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 代码: // 这种topN问题比较常见的是使用堆来解决,最小的k个 ...

  10. 剑指Offer28 最小的K个数(Partition函数应用+大顶堆)

    包含了Partition函数的多种用法 以及大顶堆操作 /*********************************************************************** ...

随机推荐

  1. Java并发编程(您不知道的线程池操作)

    Java并发编程(您不知道的线程池操作) 这几篇博客,一直在谈线程,设想一下这个场景,如果并发的线程很多,然而每个线程如果执行的时间很多的话,这样的话,就会大量的降低系统的效率.这时候就可以采用线程池 ...

  2. qt的资源替换搜索QDir具体解释

    QDir对跨平台的文件夹操作提供了非常多的便利,为了更加方便的提供全局资源的查找,QDir提供了搜索路径替换功能,攻克了资源搜索不便的问题,也能提高文件查找的效率. QDir通过已知的路径前缀去搜索并 ...

  3. 关于埃博拉(Ebola)基础研究病毒

    关于埃博拉(Ebola)病毒的基础研究 2005年.美国哈佛大学医学研究院(Harvard Medical School)James Cunningham教授关于埃博拉病毒有一项基础研究,研究成果发表 ...

  4. HDU2647-Reward(拓扑排序)

    Reward Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub ...

  5. Android 纯代码加入点击效果

    项目中非常多的Button, 同一时候配置非常多button切图,Selector是不是非常烦, 使用以下这个类,就能够直接为Button添加点击效果. 不用多个图片,不用Selector. 使用方法 ...

  6. 雷人的一幕:国外的codeproject论坛竟有人发“中文贴”.....

    潜水近一年,头一次见国人在此发“中文贴”,截图留个“纪念”....

  7. SharePoint发展 - 使用Session(代码更改webconfig)

    博客地址 http://blog.csdn.net/foxdave SharePoint启用Session能够使用Powershell,戳这里:能够改动webconfig. 本篇叙述的重点是通过fea ...

  8. Ext JS4百强应用: 做可编辑的,可checked的treegrid--第11强

    做一个可编辑的,可checked的treegrid,代码相当简洁: 请看代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN&quo ...

  9. SQL集合运算 差集 并集 交

    SQL-3标准中提供了三种对检索结果进行集合运算的命令:并集UNION:交集INTERSECT:差集EXCEPT(在Oracle中叫做 MINUS).在有些数据库中对此的支持不够充分,如MySql中只 ...

  10. win7,win8取得管理员权限 .reg文件

    Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\shell\runas] @="获取管理员所有权" " ...