查找最小的k个元素(数组)
题目:输入n个整数,输出其中最小的k个。
输入:11,6,3,7,5,8,10
题目:输入n个整数,输出其中最小的k个。
例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。
第一种方法先排序,可以用快排,然后输出前K个,o(n*logn);
第二种方法:回忆下快速排序,将待排序的数据分成两组,选取其中一个数X,左部分都比X小,右部分都比X大,如图。
则我们想要找最小的k个元素,这时,有两种可能性:
1.Sa中元素的个数小于k,则Sa中所有的数和Sb中最小的k-|Sa|个元素就是数组S中最小的K个数;
2.Sa中元素的个数大于或等于k,则需要返回Sa中最大的K个元素。
这样递归下去,不断把问题分解成更小的问题,平均时间复杂度O(n*logK).
伪代码如下,有时间在写出来运行版。
void Partition(int s)
{
int i,X;
Sa=[];//初始化为空数组
Sb=[];//初始化为空数组
Swap(s[1],s[random()%s.len]);//防止每回都相同的数
X=s[1];
for (i=2; i<=s.len; i++)
{
s[i]>X?Sa.add(s[i]):Sb.add(s[i]);
}
Sa.len<Sb.len?Sa.add(X):Sb.add(X);//X添加到长度小的那个数组中
}
void Ksmall(int *s,int k)
{
if (k<=0)
{
return;
}
if (s.len<=k)
{
输出s,可以排序,按要求
}
(Sa,Sb)=Partition(s);
Ksmall(Sa,k).add(Ksmall(Sb,k-Sa.len))
}
第三种方法;当数据N很大,100亿,这个时候数据不能全部装入内存,所以要求尽可能少地遍历所有数据。可以用容量为K的最大堆,最大堆的堆顶就是最小的K个数中的最大值,每次新进入一个数X时,如果X比堆顶的元素小,则更新,此时要更新堆,因为添加新元素X后,可能破坏最大堆的结构;如果X比堆顶的元素大,则不需要改变原来的堆 。o(n*logK);如果K还是很大,可以再分。
用数组h[]来表示堆,每个元素h[i],它的父结点是h[i/2],儿子结点是h[2*i+1]和h[2*i+2]。
#include<stdio.h>
#include<stdlib.h>
#define K 3
int h[K];
void Swap(int *a,int *b)
{
int temp;
temp=*a;
*a=*b;
*b=temp;
}
void heap(int h[],int X)
{
int p,q,temp;
if (X<h[0])
{
h[0]=X;
p=0;
while(p<K)
{
q=2*p+1;
if (q>=K)
{
break;
}
if ((q<K-1)&&(h[q+1]>h[q]))
{
q=q+1;
}
if (h[p]<h[q])
{
temp=h[q];
h[q]=h[p];
h[p]=temp;
p=q;
}
else
{
break;
}
}
}
}
int main()
{
int a[100]={11,6,3,7,5,8,10},i,max,temp;
//对h[k]个数进行赋值,且将h[0]赋值为前k个数的最大值
max=0;
h[0]=a[0];
for (i=1; i<K; i++)
{
h[i]=a[i];
if (h[max]<h[i])
max=i;
}
Swap(&h[0],&h[max]);
//对后来的数进行判断
for (i=K; i<7; i++)
{
if (a[i]<h[0])
{
heap(h,a[i]);
}
}
for (i=0; i<K; i++)
printf("%d ",h[i]);
system("pause");
}
输入:11,6,3,7,5,8,10
输出:6 5 3
输入:11,6,3,7,5,8,10
输出:5 -10 3
没有排序,如想输出结果为排序的,可在排下序。
第四个方法:如果这些数据可以明确范围,则可以用count[max]来记录每个整数出现的个数,而只需要扫描一遍就可以得到count[]数据,然后,即可寻找最小的K个数。