本题的的数组是可以包含重复元素的,且要求时间复杂度控制在O(n)
解题思路:数组中第k大的数等价于排序数组中第n-k个数,直观的想法是将数组排序后取第n-k个数即可,但是最快的排序算法时间复杂度也是O(nlogn), 可以参考快速排序一次划分的思想,将时间复杂度降低为O(n);一次划分可以讲数组分为三部分,比支点元素小或者与支点元素相等的部分,支点元素,比支点元素的部分。一次partition之后,可以得到这个支点元素的下标,并且这个下标,也就是说这个数字在数组中的位置已经确定,它就是第n-index大的数,因此我们可以在一次划分后,比较由这次划分所确定的元素的下标index和n-k比较,如果相等,则arr[index]就是第n-k个数;若index> n - k,则证明第k大的数在 index的左边,继续在左边进行一次划分,;若index < n -k,则第k大的数在index的右边,继续在右边进行一次划分,直到使得index == n- k 为止。这里运用了分治的思想。
public class Main {
public static int findKthMax(int[] arr, int k) {
if (arr == null || arr.length == 0 || k > arr.length) {
return Integer.MIN_VALUE;
}
int length = arr.length;
int start = 0;
int end = length - 1;
// 对数组进行一次划分
int index = partition(arr, start, end);
//直到index为第n-k个数为止
while (index != length - k) {
if (index > length - k) {
//说明第length - k 个数在数组的左边,继续在上次划分的左边里寻找
end = index - 1;
index = partition(arr, start, end);
} else {
//说明第length - k 个数在数组的右边,继续在上次划分的右边寻找
start = index + 1;
index = partition(arr, start, end);
}
}
return arr[index];
}
/**
* @param arr
* 带划分的数组
* @param start
* 数组的起始位置
* @param end
* 数组的结束位置 return 返回数组中某个元素的索引,索引左边的元素都比该元素小或者相等,索引右边的元素都比索引元素大
*/
public static int partition(int[] arr, int start, int end) {
if (arr == null || arr.length == 0 || start < 0 || start >= arr.length || end < 0 || end >= arr.length
|| start > end) {
return -1;
}
// 将数组的起始元素作为支点元素
int pivot = arr[start];
// left从数组最左边开始,往右移,直到找到一个比支点元素大的,待放入到数组的右半部分
int left = start;
// right从数组的最右边开始,往左移,直到找到一个比支点元素小的或者相等的,待放入到数组的左半部分
// 那么当left和right交叉时,right肯定是指向比支点元素小或者和支点元素想等的元素,此时right可以作为分界线
// right左边都是比支点元素小的或者相等的,right右边肯定是比支点元素大的
int right = end;
while (left < right) {
// left一直右移,直到找到一个比支点元素大的,待交换到数组的右边
while (arr[left] <= pivot) {
left++;
}
// right一直左移,直到找到一个比支点元素小或者和支点元素相等的元素,待交换到数组的左边
while (arr[right] > pivot) {
right--;
}
if (left < right) {
swqp(arr, left, right);
left++;
right--;
}
}
// 当left和right错开后,此时已经将数组分成两个部分,right指向之前的包括right指向的元素都和支点元素相等或比支点元素小
// left指向之后的元素包括left指向元素都比支点元素大
// 将支点元素和right指向元素进行交换,right作为数组的分割点
arr[start] = arr[right];
arr[right] = pivot;
return right;
}
private static void swqp(int[] arr, int left, int right) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
}