编程之美之寻找最大的k个数

这个题目很常见,方法也很多,这里总结了编程之美给出的几个比较好的方法,也算是对这个问题的一个总结。

方法一、partition算法,每次partition的复杂度为O(n),总的平均时间复杂度为O(nlogn)

分析:运用partition算法,如果返回的provit > k-1,则说明要找的数都在前面,把end= provit-1;如果provit < k-1,表明找到一部分,先把部分数据保存,然后start = provit + 1;如果正好相等,则保存后结束,具体代码如下:

int partition(vector<int>& numbers,int start,int end)//此partition为了满足题目的需要,把大的数据移到前面
{
	int provit = numbers[start];
	while(start < end)
	{
		while(start < end && numbers[end] <= provit)end --;
		if(start < end) numbers[start++] = numbers[end];
		while(start < end && numbers[start] >= provit) start ++;
		if(start < end) numbers[end--] = numbers[start];
	}
	numbers[start] = provit;
	return start;
}
vector<int> SearchTheKthNum(vector<int>& numbers,int k)
{
	vector<int> res;
	int start = 0,end = numbers.size()-1,i;
	while(start <= end)
	{
		int provit = partition(numbers,start,end);
		if(provit > k-1)end = provit-1;//要找的数都在前面
		else if(provit < k-1)//找到部分
		{
			for(i=start;i<=provit;i++)res.push_back(numbers[i]);
			start = provit + 1;
		}
		else//找到全部
		{
			for(i=start;i<=provit;i++)res.push_back(numbers[i]);
			break;
		}
	}
	return res;
}

方法二、堆排序

分析:建立一个大根堆,每次取堆顶元素后,调整堆,循环k次后即可,时间复杂度为O(nlogn)

void shiftDown(vector<int>& numbers,int index,int length)//向下调整堆
{
	int i = ((index+1)<<1)-1,tmp = numbers[index];
	while(i <= length -1)
	{
		if(i<length-1 && numbers[i]<numbers[i+1])i++;
		if(numbers[i] < tmp)break;
		numbers[index] = numbers[i];
		index = i;
		i = ((index+1)<<1)-1;
	}
	numbers[index] = tmp;
}
void createTree(vector<int>& numbers)//建堆
{
	int length = numbers.size(),i;
	for(i = (length>>1)-1;i>=0;i--)
	{
		shiftDown(numbers,i,length);
	}
}
vector<int> SearchTheKthNum(vector<int>& numbers,int k)
{
	int length = numbers.size(),i;
	assert(length>=0 && length >= k && k>=0);
	createTree(numbers);
	vector<int> res(k);
	for(i=0;i<k;i++)
	{
		res[i] = numbers[0];//选取当前最大元素
		numbers[0] = numbers[length-i-1];
		shiftDown(numbers,0,length-i-1);//选取后调整堆
	}
	return res;
}

方法三、计数排序(适用范围:数据的范围比较小,即数据比较集中,例如待排序的数据是一个公司的员工的年龄)

分析:首先找到待查数据的最大最小值,获得数据的范围,然后对每个元素进行计数,最后从后向前遍历查找最大的k个元素。时间复杂度为O(n),但是空间复杂度为O(Max-Min),如果数据比较分散,空间复杂度很大

void GetTheMaxAndMinValue(vector<int>& numbers,int& Max,int& Min,int length)//找到待查数据的最大最小值
{
	int i;
	for(i=1,Max = numbers[0],Min = numbers[0];i<length;i++)
	{
		if(numbers[i] > Max)Max = numbers[i];
		else if(numbers[i] < Min)Min = numbers[i];
	}
}

vector<int> SearchTheKthNum(vector<int>& numbers,int k)
{
	int length = numbers.size(),i,j=0,Max,Min;
	assert(length>=0 && length >= k && k>=0);
	vector<int> res(k);
	GetTheMaxAndMinValue(numbers,Max,Min,length);
	vector<int> count(Max-Min+1,0);
	for(i=0;i<length;i++)
	{
		count[numbers[i]-Min]++;//对每个数据进行计数
	}
	for(i=count.size()-1;j < k && i >= 0;i--)
	{
		while(count[i] > 0 && j < k)
		{
			res[j++] = i + Min;
			count[i] --;
		}
	}
	return res;
}

方法四、位比特(适用范围:数据是没有重复的整数)

分析:和方法三类似,只是这里用一个bit表示一个数据,优点就是节省空间,缺点就是只能用于没有重复数据的查找,例如查找IP、电话号码等

void GetTheMaxAndMinValue(vector<int>& numbers,int& Max,int& Min,int length)
{
	int i;
	for(i=1,Max = numbers[0],Min = numbers[0];i<length;i++)
	{
		if(numbers[i] > Max)Max = numbers[i];
		else if(numbers[i] < Min)Min = numbers[i];
	}
}
void bit_set(int& data,int index)
{
	data = (data | (0x01 << index));
}
vector<int> SearchTheKthNum(vector<int>& numbers,int k)
{
	int length = numbers.size(),i,j=0,Max,Min;
	int INTLEN = 32;
	assert(length>=0 && length >= k && k>=0);
	vector<int> res(k);
	GetTheMaxAndMinValue(numbers,Max,Min,length);
	vector<int> bit((Max-Min)/INTLEN+1,0);
	for(i=0;i<length;i++)
	{
		bit_set(bit[(numbers[i]-Min)/INTLEN],(numbers[i]-Min)%INTLEN);//遍历数据,将出现的数据的对应位置位
	}
	for(i=bit.size()-1;i>=0;i--)//查找置位的数据位中的最高的k个位
	{
		int shift = INTLEN -1;
		while(j < k && shift >= 0)
		{
			int tmp = (0x01 << shift);
			if((tmp & bit[i])!=0)
			{
				res[j++] = (i * INTLEN + shift + Min);
			}
			shift -= 1;
		}
	}
	return res;
}

以上若有不对的地方,欢迎指正,谢谢

编程之美之寻找最大的k个数

时间: 2024-11-06 09:24:36

编程之美之寻找最大的k个数的相关文章

寻找最小的k个数(update)

类似编程之美中寻找最大的k个数 解法一: 题目没有要求最小的k个数有序,也没要求最后n-k个数有序.既然如此,就没有必要对所有元素进行排序.这时,咱们想到了用选择或交换排序,即: 1.遍历n个数,把最先遍历到的k个数存入到大小为k的数组中,假设它们即是最小的k个数: 2.对这k个数,利用选择或交换排序找到这k个元素中的最大值kmax(找最大值需要遍历这k个数,时间复杂度为 O(k) ): 3.继续遍历剩余n-k个数.假设每一次遍历到的新的元素的值为x,把x与kmax比较:如果 x < kmax

编程之美之2.5 寻找最大的K个数

[题目] 有很多无序的数,从中找出最大的K个数.假定他们都不相等. [解法一] 如果数据不是很多,例如在几千个左右,我们可以排一下序,从中找出最大的K个数.排序可以选择快速排序或者堆排序 [cpp] view plaincopy #include<stdio.h> #include<stdlib.h> int cmp(const void *a,const void *b){ return *(int *)a - *(int *)b; } int main(){ int n,k;

编程之美2.5 寻找最大的K个数

在一个数组中寻找最大的K个数,我们首先说一种非常简单的方法,利用快速排序中的分割算法,即我们经常看见的partition.这个函数会返回一个 int 类型的值,这个值代表的是前一半数字和后一半数字的分割点,前一半数字都小于等于后一半数字(递增排序),所以,我们只要找到相对应的分割点,即可以找到最大的K个数,或者最小的K个数,这就是利用线性方法可以完成任务的方法. 首先,给出函数声明: int DutPartition(int*, int, int); void DutFindMaxKInArra

寻找最小的K个数

寻找最小的K个数 题目描述:查找最小的K个数 题目:输入n个整数,输出其中最小的K个数 例如,输入1.2.3.4.5.6.7.8这8个数字,则最小的4个数字为1.2.3.4. 第一节.各种思路,各种选择 要求一个序列中最小的K个数,按照惯有的思维方式,很简单,先对这个序列从小到大排序,然后输出前面的最小的K个数即可: 至于选取什么样的排序方法,第一时间应该想到的是快速排序,我们知道,快速排序平均时间复杂度为O(nlogn),然后再遍历序列中前K个元素输出,即可,总的时间复杂度为O(nlogn +

寻找最大的K个数

编程之美有一道考察多种排序的题目,题目如下: 有一个长度为N的无序数组,假定其中的每一个元素都各不相等,求其中最大的K个数. 作者对于此题目结合各种排序算法给出了五种解法思路. 解法一: 使用快速排序或堆排序对它们元素进行排序,整个排序的时间复杂度为O(N*log2N),然后取出前K个,时间复杂度为O(K),总时间复杂度O(N*log2N)+O(K)=O(N*log2N).根据题目要求,并不需要后N-KN-K个数有序,而只需要寻找最大K的个数,因此可以用选择排序和交换排序进行部分排序来筛选最大的

寻找最大的K个数(下)

接着昨天的写,里面的代码包含昨天的 1 #include <iostream> 2 using namespace std; 3 #define N 50 4 5 //初始化数组 6 int a[] = {6, 2, 3, 4, 4, 3, 1, 2, 4, 4}; 7 //int a[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; 8 //int a[] = {1, 2, 3, 4, 5, 6}; 9 //int a[] = {6, 2, 3, 9, 4, 10,

【编程之美】2.5 寻找最大的k个数

有若干个互不相等的无序的数,怎么选出其中最大的k个数. 我自己的方案:因为学过找第k大数的O(N)算法,所以第一反应就是找第K大的数.然后把所有大于等于第k大的数取出来. 写这个知道算法的代码都花了2个多小时,反思,太慢了. 注意边界条件,不要混混沌沌的. /************我自己的解法*****************/ //选择数组a[N]中最大的第k个数 int Select(int * a, int N, int k) { if(k > N || a == NULL) { cou

寻找最小的k个数(四种方法)

1 使用从大到小的优先队列保存最小的K个数,每次取出K个数之后的其余数和堆顶元素比较,如果比堆顶元素小,则将堆顶元素删除,将该元素插入 void topK(int arr[],int n,int k) { if(k>n) return; priority_queue<int> q; for(int i=0;i<n;++i) { if(q.size()<k) q.push(arr[i]); else { if(arr[i]<q.top()) { q.pop(); q.pu

【编程之美】寻找数组中的最大值和最小值

数组是最简单的一种数据结构.我们经常碰到的一个基本问题,就是寻找整个数组中最大的数,或者最小的数.这时,我们都会扫描一遍数组,把最大(最小)的数找出来.如果我们需要同时找出最大和最小的数呢? 对于一个由N个整数组成的数组,需要比较多少次才能把最大和最小的数找出来呢? 分析与解法 解法一:分别求最大和最小值 可以分别求出数组的最大值和最小值,这样,我们需要比较2N次才能求解. 解法二:分组求解 (1) 按顺序将数组中相邻的两个数分在同一组: (2) 比较同一组的两个数,将大的数放在偶数位上,小的放