Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
Example 1:
Input: [3,2,1,5,6,4]
and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6]
and k = 4
Output: 4
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
以下为各个算法相应的代码
int findKthLargest(int* nums, int numsSize, int k) {
int i,j;
int t;
int max;
/*
1、冒泡法********************************************************************************************
//通过从左到右不断交换相邻逆序的相邻元素,在一轮的交换之后,可以让未排序的元素上浮到最右侧,是的右侧是已排序的。在一轮交换中,如果没有发生交换,就说明数组已经是有序的,此时可以直接退出。
for(i=0;i<numsSize-1;i++)
for(j=1;j<numsSize-i;j++)
{
if(nums[j]>nums[j-1]){
int t=nums[j];
nums[j]=nums[j-1];
nums[j-1]=t;
}
}
return nums[k-1];
*/
/*
2、插入排序******************************************************************************************
//插入排序从左到右进行,每次都将当前元素插入到左侧已经排序的数组中,使得插入之后左部数组依然有序。第 j 元素是通过不断向左比较并交换来实现插入过程:当第 j 元素小于第 j - 1 元素,就将它们的位置交换,然后令 j 指针向左移动一个位置,不断进行以上操作。
for(i=0;i<numsSize-1;i++){
for(j=i+1;j>0;j--){
if(nums[j]>nums[j-1]){
t=nums[j];
nums[j]=nums[j-1];
nums[j-1]=t;
}
else{
break;
}
}
}
return nums[k-1];
*/
/*
3、选择排序,每次选择最大的排在第k个位置****************************************************************
//选择出数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
for(i=0;i<k;i++)
{
t=i;
max=nums[i];
for(j=i+1;j<numsSize;j++){
if(nums[j]>max){
t=j;
max=nums[j];
}
}
int a=nums[i];
nums[i]=max;
nums[t]=a;
}
return nums[k-1];
*/
/*
4、归并排序*****************************************************************************************
if(numsSize==1)
return nums[0];
sort_2(nums,0,numsSize-1);
return nums[k-1];
*/
/*
5、希尔排序*****************************************************************************************
//对于大规模的数组,插入排序很慢,因为它只能交换相邻的元素,每次只能将逆序数量减少 1。
//希尔排序的出现就是为了改进插入排序的这种局限性,它通过交换不相邻的元素,每次可以将逆序数量减少大于 1。
//希尔排序使用插入排序对间隔 h 的序列进行排序。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。
int gap; //跳跃的步数
for(gap=numsSize/2;gap>=1;gap=gap/2){
for(i=0;i<numsSize-gap;i++){
for(j=i+gap;j-gap>=0;j=j-gap){ //对gap内的数组进行插入排序
if(nums[j]>nums[j-gap]){
int aaa=nums[j];
nums[j]=nums[j-gap];
nums[j-gap]=aaa;
}
}
}
}
return nums[k-1];
*/
/*
6、快速排序**************************************************************************************
sort(nums,0,numsSize-1);
return nums[k-1];
*/
/*
7、基于切分的快速选择排序************************************************************************
//时间复杂度O(n)
//该算法是线性级别的,因为每次正好将数组二分,那么比较的总次数为 (N+N/2+N/4+..),直到找到第 k 个元素,这个和显然小于 2N。
int low=0;
int high=numsSize-1;
while(high>low){
int q;
q=splite_num(nums,low,high); //获得切分的下标,并排好序
if(q==k-1){
break;
}
if(q>k-1){
high=q-1;
}
else{
low=q+1;
}
}
return nums[k-1];
*/
/*
8、堆排序********************************************************************************************
// 参考 https://www.cnblogs.com/chengxiao/p/6129630.html
//1.构建大顶堆
for(int i=numsSize/2-1;i>=0;i--){
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(nums,i,numsSize);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for(int j=numsSize-1;j>0;j--){
swap(nums,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(nums,0,j);//重新对堆进行调整
}
return nums[numsSize-k];
*/
//********************************************************************************************
}
#################################################################################################
void adjustHeap(int *arr,int i,int length){ //1.构建大顶堆
int temp = arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
k++;
}
if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
/**
* 交换元素
* @param arr
* @param a
* @param b
*/
void swap(int *arr,int a ,int b){ //交换元素
int temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
###############################################################################################
void sort(int *nums,int low,int high){ //快速排序
//•归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;
//•快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
if(high<=low)
return;
int q;
q=splite_num(nums,low,high); //获得切分的下标,并排好序
sort(nums,low,q);
sort(nums,q+1,high);
}
int splite_num(int *nums,int low,int high){ //获得切分的下标,并排好序
//取 a[low] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素,并不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[low] 和 a[j] 交换位置。
int a=nums[low];
int i=low+1;
int j=high;
while(1){
while(i<high && nums[i]>=a) //搜索nums[i]<a,当 nums[i]==a的时候,可以分配到另一个数组里,保证公平分配
{
i++;
}
while(j>low && nums[j]<=a) //搜索nums[j]>a,当 nums[i]==a的时候,可以分配到另一个数组里,保证公平分配
{
j--;
}
if(i>=j){
break;
}
else{
int ttt=nums[i];
nums[i]=nums[j];
nums[j]=ttt;
i++;
j--;
}
}
int ttt=nums[low];
nums[low]=nums[j];
nums[j]=ttt;
return j;
}
##############################################################################################
int sort_2(int *nums,int low,int high){ //归并排序
// //归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。自顶向下归并排序
if(high<=low)
return;
int middle=low+(high-low)/2;
sort_2(nums,low,middle);
sort_2(nums,middle+1,high);
merge(nums,low,middle,high); //合并两个降序的数组
}
void merge(int *nums,int low,int middle,int high){ //合并两个降序的数组
int i,j;
i=low;
j=middle+1;
int k=0;
int b[high-low+1];
while((i<=middle)&&(j<=high)&&(k<high-low+1)){
if(nums[i]<nums[j]){
b[k]=nums[j];
j++;
}
else{
b[k]=nums[i];
i++;
}
k++;
}
if(i>middle && j<=high){
for(;j<=high;j++){
b[k]=nums[j];
k++;
}
}
if(j>high && i<=middle){
for(;i<=middle;i++){
b[k]=nums[i];
k++;
}
}
for(i=low;i<=high;i++)
nums[i]=b[i-low];
}
#############################################################################################