寻找数组最大K个数

问题描述

给定一个无序数组,找出最大的K个数。 以下方法可以解决此类问题。

1、基于快排Partition

快速排序中,其中最重要的一步是如何找到某个基准值的最终在数组的存放位置index,找到这个位置index后,则index左边的值全小于等于基准值,index右边的值全大于基准值。在本题中要找TOP K大的数,那么过程必须反着来,也即index左边大于基准值,右边小于基准值。
那么我们可以通过比较这个index和K值进行比较:

   (1)若Sa组的个数大于或等于K,则继续在Sa分组中找取最大的K个数字 。
   (2)若Sa组中的数字小于K ,其个数为T,则继续在Sb中找取 K-T个数字 。

首先需要找到基准值的位置,通过Partition函数实现:

int Partition(int a[], int left, int right)
{
	int i,j,tmp;  
    i=left;j=right;tmp=a[i]; //保存当前基准值  

    while(i<j) //本次划分终止条件  
    {  
        while(i<j && a[j]<tmp) //从后往前找,如果比基准值大,则j--  
            j--;  
          
        if(i<j)  
        {  
            a[i]=a[j];  //将小于等于基准值的a[j],填充到前一个位置a[i]  
            i++;  
        }  
          
        while(i<j && a[i]>tmp) //从前往后找,如果比基准值小,则i++</span>  
            i++;  
          
        if(i<j)  
        {  
            a[j]=a[i];  //将大于等于基准值的a[i],填充到前一个位置a[j]  
            j--;  
        }  
    }  
    a[i]=tmp; //找到本次基准值的最终位置
	
	return i;
}

找到基准值位置之后,我们就可以进行递归子数组查找
int FindTopKNums(int a[], int left, int right, int K)
{
	int index=-1;
	if(left<right)
	{
		int idx=Partition(a,left,right);
		int len=idx-left+1; //该子数组中,到该次划分的位置,总共有len个元素
		
		if(len==K) <span style="white-space:pre">  //找到TOP K的元素下标
			index=idx;
		else if(len<K)      //如果元素个数不足K个,则在划分的右数组进行查找,此时只需要找TOP K-len
	 		index=FindTopKNums(a,idx+1,right,K-len);
		else<span style="white-space:pre">   //如果划分的左数组元素个数大于K个,此时只需要在这个数组里面找TOP K
			index=FindTopKNums(a,left,idx-1,K);
	}
	return index;
}

求得TOP K元素
int main()
{
	int a[]={1,2,4,5,3};
	int K=3;
	int index=FindTopKNums(a,0,4,K);
	
	for(int i=0;i<K;i++)
		printf("%d ",a[i]);
	
	return 0;
}

值得一提的是:考虑到某些数组元素的特殊性,可以采用Random Partition,也就是进行划分的时候,并不是从该数组的首元素开始,而是随机一个下标位置开始
//随机划分函数
int RandomPartition(int a[],int left,int right)
{
	srand((int)time(0));
    int i = left+rand()%(right-left+1);//产生随机数
    int temp = a[i];
    a[i] = a[left];
    a[left] = temp;
    return Partition(a,left,right);//调用划分函数
}

2、基于堆排序

上面基于快排Partition的方法,有一个缺点,就是需要比较很多次,而堆排序正是可以减少比较的次数。
该方法的思想:用容量为K的最小堆来存储最大的K个数,首先对这K个元素的数组建最小堆,那么堆顶元素a[0]是这K个元素里面最小的,接着遍历原始数组a的剩余元素,如果比堆顶的元素大,则将其替换堆顶元素,并重新调整此K个元素构成的最小堆。

堆调整:
//该函数调整下标k对应的元素a[k]在小顶堆的位置,下标从0开始 
void MinHeapSift(int a[], int k, int n)
{
	int i,j,temp;
	i=k;
	j=2*i+1;
	temp=a[k];
	
	while(j<n)
	{
		if(a[j+1]<a[j]) //找出左右孩子中较小者 
			j++;
		if(temp<=a[j])  //不需要调整 
			break;
		
		a[i]=a[j];
		i=j;
		j=2*i+1;
	}
	
	a[i]=temp;
}

建堆:
void BuildHeap(int a[], int n)
{
	for(int i=n/2-1;i>=0;i--)
		MinHeapSift(a, i, n);
}

遍历原始数组,并维护TOP K数组
void FindTopKNums(int a[], int topK[], int n, int K)
{
	for(int i=0;i<K;i++)
		topK[i]=a[i];
		
	BuildHeap(topK, K); //建堆
	
	for(int i=K;i<n;i++)
	{
		if(a[i]>topK[0])
		{
			topK[0]=a[i];
		 	MinHeapSift(topK,0,K); //调整
		}
	}
}

int main()
{
	int a[]={1,2,4,5,3};
	int topK[4];
	int K=4;
	FindTopKNums(a,topK,5,K);
	
	for(int i=0;i<K;i++)
		printf("%d ",a[i]);
	
	return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值