分析:
方法一:排序法,先把这个无序数组进行排序,假设按照从小到大排,则第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;
}
运行结果如图: