问题
给出一个数组,求数组中第k小的元素。
解析
- 首先,随机在数组中选择一个数作为划分值。然后,进行快排,将小于划分值的数放到数组左边,等于划分值的数放到数组中间,大于的放到数组右边,然后判断k与等于划分值区域的相对关系,如果k正好在等于区域,那么数组第k小的数就是划分值,如果k在区域的左边,那么我们递归对左边再进行上述过程,右边也一样。
- 在选取划分值时:
(1)我们将数组每5个相邻的数分成一组,后面的数如果不够5个数也分成一组。
(2)对于每组数,我们找出这5个数的中位数,将所有组的中位数构成一个mid数组。
(3)我们再求这个中位数数组中的中位数,此时所求出的中位数就是那个number。
(4)通过这个number进行快速排序过程。
设计
int select_kth_smallest(int root[], int begin ,int end,int k){
if(begin==end){//数组中只有一个数
return root[begin];
}
int div=findMidOfMid(root, begin, end);
partation(root, begin, end, div);
if(k>=a[0]&&k<=a[1]){//如果需要求的数正好在等于区域,直接返回root[k]
return root[k];
}
else if(k<a[0]){//要找的数比divide小,递归求前半部分
return select_kth_smallest(root, begin, a[0]-1, k);
}
else{//要找的数比divide大,递归求后半部分
return select_kth_smallest(root, a[1]+1, end, k);
}
}
分析
假设总数组的数字个数是N,那么中位数数组中数字的个数就是 N / 5。中位数数组中有一半的数比这个中位数中的中位数大,所以总共有 N / 10个数比这个中位数中中位数大。加上在比大于中位数中的中位数大的中位数大的数,所以至少有N / 10 + ( 2N ) / 10 = ( 3N ) / 10 个数比中位数中的中位数大。那么,经过快速排序,最坏情况也能够使选区在数组的 ( 3N ) / 10 或者 ( 7N ) / 10 的位置。所以,时间复杂度为O(N)。
源码
源码在这里
欢迎批评指正~