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

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

首先,给出函数声明:

int DutPartition(int*, int, int);
void DutFindMaxKInArray_1(int*, int, int);

源代码如下:

/*经典的快排分割程序*/
int DutPartition(int* A, int low, int high)
{
	/*哨兵,这里其实也可以用rand生成一个随机值,避免效率最低化*/
	int pivot = A[low];

	while (low < high)
	{
		while (low < high && A[high] >= pivot)
			--high;
		A[low] = A[high];

		while (low < high && A[low] <= pivot)
			++low;
		A[high] = A[low];
	}

	A[low] = pivot;
	/*最终返回“中点”位置*/
	return low;
}

/*这里的解法很简单,就是一直寻找index的最终位置,找到了就可以跳出循环了*/
void DutFindMaxKInArray_1(int* A, int size, int k)
{
	if (!A || size <= 0 || k < 1 || size < k)
		return;

	int index = DutPartition(A, 0, size - 1);

	/*寻找最终位置*/
	while (index != size - k)
	{
		if (index < size - k)
			index = DutPartition(A, index + 1, size - 1);
		else
			index = DutPartition(A, 0, index - 1);
	}

	/*输出最大的K个值*/
	for (int i = size - k; i < size; ++i)
		cout << A[i] << " ";
	cout << endl;
}

经过分析代码,我们可以知道,这个方法会改变原来数组中数字的顺序。假如说,不允许改变原来的结构呢?那么,我们可以利用最小堆解决TOPK的问题,最小堆的性质是堆顶元素是堆中元素值最小的一个,所以,我们只要判断下当前的堆顶元素是否比数组中元素小就可以了,如果是小,那么替换并重新调整堆,如果是大,那么不用理会这个元素。

由于 stl 中 multiset 是利用红黑树实现的,可以实现堆的性质,所以,我们可以利用 multiset 来解决这个问题,这种方法得到的时间复杂度是O(nlogn)

函数声明如下:

/*2.5 寻找最大的K个数*/
typedef std :: multiset<int, std :: less<int>>					intSet;
typedef std :: multiset<int, std :: less<int>> :: iterator		setInterator;
void DutFindMaxKInArray_2(const std :: vector<int>&, intSet&, int);

源代码如下:

/*第一种解法不好的地方在于它改变了数组中原来数的顺序*/
/*
 *第二种解法利用最小堆的思想寻找TOPK,是经典的大数据解题方法,
 *网络上很多类似的解释,这里,不做过多的解释
 */
void DutFindMaxKInArray_2(const std :: vector<int>& data, /*模拟最小堆*/intSet& leastNums, int k)
{
	leastNums.clear();

	if (k < 1 || data.size() < k)
		return;

	vector<int> :: const_iterator iter = data.begin();
	for (; iter != data.end(); ++iter)
	{
		if (leastNums.size() < k)
		{
			leastNums.insert(*iter);
		}
		else
		{
			setInterator it = leastNums.begin();

			if (*iter > *it)
			{
				leastNums.erase(it);
				leastNums.insert(*iter);
			}
		}
	}
}
时间: 2024-08-29 20:22:44

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

编程之美3:寻找数组中的最大值和最小值以及最大值和次大值

很开心,这是今天的第三篇文章啦!下午健身也感觉非常过瘾,托付宿舍妹子从日本代购的护肤品也到了.耳边漂浮着Hebe田馥甄的<魔鬼中的天使>文艺的声线,一切都好棒,O(∩_∩)O哈哈~.爱生活,爱音乐,爱运动,额,当然还有要爱学习啦!加油(^ω^) 额,扯远了.第三篇是关于寻找数组中的最大值和最小值.第一次看到这个题目的时候,楼主稍微鄙视了一下,因为觉得这个题目有什么好做的.但是楼主还是看了看<编程之美>上的写的,发现还是有必要记录一下,不一样的思考方式.很赞!大家和楼主一起哦,Are

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

这个问题其实很容易解决,就是循环遍历一遍数组,然后找到数组中存在的最大值和最小值就可以了,书中主要讨论的问题是比较次数较小的方法,不过,书中已经证明了,无论用什么方法最少的比较次数也就是循环遍历一遍的比较,结果是O(1.5N)的,所以,很容易的可以解决这个问题. 第一种方法: 函数声明: void DutFindMaxAndMinInArray_1(int*, int, int&, int&); 源代码如下: /*基本的解法寻找最大值和最小值*/ bool _DutFindMaxAndMi

编程之美2.3——寻找水军(抵消法)

1.在数组中寻找出现次数超过一半的一个元素. 2.在数组中寻找出现次数超过1/4的三个元素. [思路] 1)常规做法:先将数组排序,时间O(nlogn):再遍历一次,统计每个元素出现的次数,得到题目要求. 2)时间O(n)的做法:抵消法.对于第一题,每次抵消两个不同的数,剩下的数组主元素出现次数还是超过一半.即缩小题目规模的思想. 对于第二题,则每次抵消4个数,转化为规模较小的问题. 抵消两个数的代码很简单: int findOverHalf(int *ID, int N){ int ntime

编程之美2.3 寻找发帖水王

这道题目由于不容易写测试用例,所以,可以把题目转换为:在一个数组中,有一个数字出现的次数超过了数组大小的一半,这和题目原意是一样的. 这道题目的思想是我们同时去掉数组中两个不一样的数字,那么,数组中原来存在的规律是不变的(仅针对这个题目). 好吧,还是先给出函数声明: /*2.3 寻找发帖水王*/ bool DutVerify(int*, int, int); int DutFindNumMoreThanHalf(int*, int); 可以看到,函数声明中多了一个:DutVerify,这个函数

编程之美2.3: 寻找发帖水王

题目:传说,Tango有一大"水王",他不但喜欢发帖,还会回复其他ID发的帖子,发帖数目超过帖子总数的一半,如果你有一个当前论坛上所有帖子的列表,其中帖子作者的ID也在表中,你能快速找到这个传说中的Tango水王吗? 解题思路:由于水王的发帖数目超过一半,当每次删除两个不同ID的帖子时,水王占得帖子数目仍然大于剩下帖子的一半,重复整个过程,将ID列表中的ID总数降低,转化为更小的问题,从而得到最后水王的ID. #include <iostream> #include <

编程之美2.14 求数组的子数组之和的最大值

问题描述: 一个有N个整数元素的一维数组(A[0], A[1], A[2],...,A[n-1]),这个数组当然有很多子数组,那么子数组之和的最大值是什么呢? 解法: 1. 暴力解法-------O(N^3) 2. 改进版暴力解法-------O(N^2) *3. 分治算法-------O(NlogN)(暂时未去实现) 4. 数组间关系法-------O(N) 具体思路和代码: 1.暴力解法 思路:Sum[i,...,j]为数组第i个元素到第j个元素的和,遍历所有可能的Sum[i,...,j].

编程之美2.17 数组循环移位

问题描述: 设计一个算法,把一个含有N元素的数组循环左移或者右移K位. 解决方法: 1. 暴力解法------O(KN) 2. 颠倒位置------O(N) 具体思路和代码: 1. 暴力解法------O(KN) 思路:循环K次,每次移动一位 代码: 1 //右移 2 void s1(int A[], int n, int k) 3 { 4 k = k % n; 5 for(int i = 0; i < k; i++) 6 { 7 int t = A[n-1]; 8 for(int j = n-

编程之美leetcode之编辑距离

Edit Distance Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.) You have the following 3 operations permitted on a word: a) Insert a character b) Delete a char

编程之美2.17之数组循环移位

题目描述:设计一个算法,把一个含有N个元素的数组循环右移K位,要求算法的时间复杂度位O(Log2N),且只允许使用两个附加变量. 什么意思呢,就是说如果输入序列为:abcd1234,右移2位即变为34abcd12.唯一的要求就是使用两个附加变量. 其实这道题编程珠玑上面也出现过,书中给出的一种符合题意的解法是巧妙地进行翻转.以把abcd1234右移4位为例: 第一步:翻转1234,abcd1234---->abcd4321 第二步:翻转abcd,abcd4321---->dcba4321 第三