5.查找最小的k个元素(数组)

查找最小的k个元素(数组)
题目:输入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个数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值