求取第K大的数或者前K大的数

本文探讨了几种有效的算法来找到数组中的最大K个数,包括使用排序、堆、快速选择以及multiset等方法,并对比了它们的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    这个题目比较经典,本文讨论几个常见方法:

方法一:将数据读入数组然后排序,当然这种办法最容易理解,但事件复杂度不忍直视:O(N^2)。同时,当面试时被问及海量数据,这个则不能满足,因为可能内存中根本放不下这么多数据,同时计算机会运行超时。


方法二:在不断判断数据过程中只保存一个容量为K的数组,将这个数组进行排序,最后输出就可以了。如果只是简单地实现,则时间复杂度是O(N *K)。

                若采用二叉树实现则在O(logK)时间内完成删除操作,最终时间复杂度是O(N*logK)。考虑堆、快排或者红黑树,若采用红黑树,则只需要调用STL中的multiset。

利用最大堆实现:

    堆排序:

    获得左子孩子:

int leftchild(int i )
{
	return(2*i+1);
}
template<typename Comparable>


    “下滤”调整堆:

void gercdown(vector <Comparable> &a, int i, int n)
{
	int child = leftchild(i);
	Comparable temp = a[i];
	for(; leftchild(i) < n; i = child)
	{
		child = leftchild(i);
		if (child < n -1&&a[child] < a[child + 1])
		{
			child++;
		}
		if(temp < a[child])
			a[i] = a[child];
		else
			break;
	}
	a[i] = temp;
}

    建堆:

template<typename Comparable>
void HeapBuild(vector <Comparable> &a)
{
	for(int i = a.size()/2 - 1; i >= 0; i--)
	{
		gercdown(a, i, a.size());

	}
 }

   获得最大的K个数:

void HeapDelete(vector <Comparable> &a, int length)
{
	
 	for(int j = length; j > 0; j--)
	{
		int len = j -1;
		swap(a[0],a[len]);
		gercdown(a, 0, len);
	}
}


    在数组a中倒数第K个数就是第K大的数。


    利用快速排序的方法称为快速选择,不同于快排的地方是:若将集合S划分为S1和S2,则:

    若K = 1 + S1,那么key值就是第K个最大元;

    若K>=S1,那么第K个最大元必然在S2中,否则在S1中。

    若能够根据以上规则判断出范围则不需要对另一半进行排序。

    快速选择代码:

template<typename Comparable>
int median3(vector<Comparable> &a, int left, int right)
{
	int center = (left + right) / 2;
    if(a[center] < a[left])
		swap(a[left],a[center]);
	if(a[center] > a[right])
		swap(a[right],a[center]);
	if(a[center] < a[left])
		swap(a[center], a[left]);
	swap(a[center], a[right - 1]);
	return a[right - 1];
}


template<typename Comparable>
void quickSelect(vector<Comparable> &a, int left, int right, int k,int N)
{
	Comparable key = median3(a ,left, right);
	
	int i = left, j = right - 1;
	while(1)
	{
		while(a[++i] < key){}
		while(a[--j] > key){}
		if(i < j)
		{
			swap(a[i], a[j]);
		}
		else
			break;
	}
	swap(a[i], a[right - 1]);
	if(k > N - i)
		quickSelect(a, left, i - 1, k, N);
	else if(k < N -i)
		quickSelect(a, i+1, right, k, N);
	else
	{
		cout<<a[i];
		return;
	}
}


  

    若使用multimet则直接调用STL的函数即可

    

    综上所述,还是堆最方便,而且便于理解,当然实际运行的话,当数据量很大时,快排效果好于堆(当key值取得理想)

    细节过几天记得总结一下!!!!


    似乎还有个基数排序方法,但是空间复杂度太高了O(10N),时间复杂度是O(N)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值