第 K 大的数

第 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)
原文请点击此处
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值