第 K 大的元素
问题
问题描述:
- 在数组中找到第 k 大的元素
挑战:
- 要求时间复杂度为 O(n),空间复杂度为 O(1)
解决
这个问题其实与数组排序类似,可以使用 k 趟冒泡排序或选择排序,选出数组中前 k 大的元素并排序,就可以得到数组中第 k 大的数。
每一趟选择排序或冒泡排序,都会选出数组中最大的元素,所以第 k 趟就会选择出第 k 大的元素
代码如下(使用选择排序)
//解决方案一:使用选择排序算法
public static int solutionOne(int k, int[] nums) {
if (nums == null || nums.length == 0 || k < 1 || k > nums.length) {
return -1;
}
for (int i = 0, temp; i < k; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] < nums[j]) {
temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums[k-1];
}
利用选择排序的算法,对数组进行部分排序。
对数组进行 k 趟选择排序,找出数组中第 k 大的元素,其时间复杂度为 O(kn),最坏的情况是 O(n^2) 当 k = n 时。
可以对其优化,当 k > n/2 转化为求数组中第 n-k 小的数,这样时间复杂度最坏的情况是 O(n^2 / 2)。
使用这种算法,并不能满足上面的挑战;所以我们需要一个更高效的办法!
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序
一趟快排通过从数组中随机选择一个元素作为基准,使数组中元素划分为两个部份,一部份都大于基准元素,另一部份都小于基准元素,而此时基准元素也放到了数组中正确的位置。只需要进行若干次快排,直到基准元素的下标为 k-1,那么此时该基准元素就是数组中第 k 大的元素。
代码如下
//解决方案二:使用快速排序
public static int solutionTwo(int k, int[] nums) {
if (nums == null || nums.length == 0 || k < 1 || k > nums.length) {
return -1;
}
return partition(k-1, 0 ,nums.length-1, nums);
}
public static int partition(int k, int start, int end, int[] nums) {
if (start == end) {
return nums[start];
}
int i = start, j = end;
int temp = nums[(i+j)/2], judge = 0;
while (i < j) {
while (i < j && nums[i] >= temp) i++;
while (i < j && nums[j] <= temp) j--;
if (i < j) {
judge = nums[i];
nums[i] = nums[j];
nums[j] = judge;
i++;
j--;
}
}
if (i < k) {
return partition(k, i+1, end, nums);
} else if (i > k) {
return partition(k, start, i - 1, nums);
} else {
return nums[i];
}
}
利用快速排序,每次从数组中随机选取一个数,然后以该数为基准对数组进行划分,将比该数大的数放到该数的前面,将比该数小的数放到该数的后面,然后判断该数下标是否等于 k-1;如果等于则返回该数作为结果。否则则继续判断该数下标是否大于 k-1,如果大于,则将数组中该数的前一部分继续划分,如果小于则将后一部分继续划分,直到得到结果。
- 时间复杂度:n + n/2 + n/4 + … => 2n => O(n)
原文请点击此处 |
---|