寻找无序数组中的第K大元素

分析:
方法一:排序法,先把这个无序数组进行排序,假设按照从小到大排,则第k个元素就是数组中的第k大元素。
排序的时间复杂度最快为o(nlogn)

下面用快速排序实现:

#include<iostream>
template<typename T>
T partition(T* arr,int low,int high)
{
	int tmp=arr[low];
	while(low<high)
	{
		while(low<high&&arr[high]>=tmp)
		{
			high--;
		}
		arr[low]=arr[high];
		while(low<high&&arr[low]<=tmp)
		{
			low++;
		}
		arr[high]=arr[low];
	}
	arr[low]=tmp;
	return low;
}
template<typename T>
void Quick(T *arr,int left,int right)
{
	if(left<right)
	{
		int par=partition(arr,left,right);
		Quick(arr,left,par-1);
		Quick(arr,par+1,right);
	}
}
template<typename T>
void QuickSort(T *arr,int len)
{
	Quick(arr,0,len-1);
}

int main()
{
	int arr[]={1,43,53,6,67,68,65,42,63,62,6,4,61,5,2,51};
	int len=sizeof(arr)/sizeof(arr[0]);
	QuickSort(arr,len);
	for(int i=0;i<len;i++)
	{
		std::cout<<arr[i]<<" ";
	}
	std::cout<<std::endl;
	std::cout<<"数组中第5大元素为:"<<arr[len-5]<<std::endl;
	std::cout<<"数组中第1大元素为:"<<arr[len-1]<<std::endl;
	return 0;
}

输出结果如图所示:
在这里插入图片描述
方法二:可以建立一个二叉堆(小根堆),容量为K,堆上的k个节点代表当前最大的K个元素,遍历原数组,每遍历一个元素,就和堆顶进行比较,如果当前元素小于等于堆顶,就继续遍历;如果大于堆顶,把他放到堆顶替换原来的堆顶元素,再调整二叉堆,遍历结束后,堆顶就是数组的最大k个元素中的最小值,也就是第K大元素。
具体操作:
1.把数组的前k个元素构建成堆。
2.遍历数组与堆顶进行比较,如果小于等于堆顶,继续遍历;如果大于堆顶,则取堆顶元素并调整堆。
3.此时的堆顶,就是堆中的最小值,也就是数组中的第K大元素。
(注:以下代码是在数组允许修改的情况下,去原地交换来创建的二叉堆,若数组不允许修改可单独创建一个空间为k的堆,将结果与原数组分开)此算法的时间复杂度为当k<n时,o(logk)

void downAdjust(T* arr,int index,int len)//调整堆的结构
{
	T tmp=arr[index];
	int childindex=index*2+1;//先保存父节点的值
	while(childindex<len)
	{
		if(childindex+1<len&&arr[childindex+1]<arr[childindex])//定位到两个孩子中比较小的哪一个
		{
			childindex++;
		}
		if(tmp<=arr[childindex])
			break;//父节点小于孩子结点不需要调整。
		arr[index]=arr[childindex];
		index=childindex;
		childindex=2*childindex+1;
	}
	arr[index]=tmp;
}
template<typename T>
void BuildHeap(T* arr,int len)//构建堆
{
	for(int i=(len-2)/2;i>0;i--)
	{
		downAdjust(arr,i,len);
	}
}
template<typename T>
T FindNumberK(T *arr,int len,int k)//找数组中的第K大元素
{
	BuildHeap(arr,k);
	for(int i=k;i<len;i++)
	{
		if(arr[i]>arr[0])
		{
			arr[0]=arr[i];
			downAdjust(arr,0,k);
		}
	}
	return arr[0];
}
int main()
{
	int arr[]={1,43,53,6,67,68,65,42,63,62,6,4,61,5,2,51};
	int len=sizeof(arr)/sizeof(arr[0]);
	for(int i=0;i<len;i++)
	{
		std::cout<<arr[i]<<" ";
	}
	std::cout<<std::endl;
	std::cout<<"数组中第5大元素为:"<<FindNumberK(arr,len,5)<<std::endl;
	return 0;
}

输出结果如图所示:
在这里插入图片描述
方法三:采用分治法,在寻找第K大元素时,可以利用这个思路,以某个元素为基准,把大于它的元素都换到数组左边,小于它的元素换到数组右边。
以下代码是基于划分法(一次划分去实现的)此算法的时间复杂度为o(n)

template<typename T>
void swap(T*arr,int a,int b)
{
	T tmp=arr[a];
	arr[a]=arr[b];
	arr[b]=tmp;
}
template<typename T>
T partition(T* arr,int low,int high)
{
	int b=rand()%(high-low+1)+low;//随机选取基准
	swap(arr,low,b);
	T tmp=arr[low];
	while(low<high)
	{
		while(low<high&&arr[high]>=tmp)
		{
			high--;
		}
		arr[low]=arr[high];
		while(low<high&&arr[low]<=tmp)
		{
			low++;
		}
		arr[high]=arr[low];
	}
	arr[low]=tmp;
	return low;
}
template<typename T>
T FindNumberK(T* arr,int left,int right,int k)
{
	int index=partition(arr,left,right);//执行一次划分
    if(k<right-index+1)//右边的数字比K多
	{
		return FindNumberK(arr,index+1,right,k);//在右边找	
	}
	else if(k>right-index+1)
	{
		return FindNumberK(arr,left,index-1,k-(right-index+1));//否则在左边找左边的第,k-(right-index+1大的元素
	}
	else
	{
		return arr[index];
	}

}

int main()
{
	int arr[]={1,43,53,6,67,68,65,42,63,62,6,4,61,5,2,51};
	int len=sizeof(arr)/sizeof(arr[0]);
	std::cout<<"数组中第5大元素为:"<<FindNumberK(arr,0,len-1,5)<<std::endl;
	return 0;
}

运行结果如图:
在这里插入图片描述

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值