前言
今天的文章分享如何在乱序的数组中,找到第 K 大的数
之前的文章也分享了一篇关于找数组中最小的 k 个数的内容,还要求复杂度为 logn。那今天为什么还要介绍复杂度更高的算法呢。其实之前的算法复杂度低是建立在一个排序二叉树上面的。在排序二叉树上找任何的数据复杂度都是 logn。而今天的算法是建立在乱序的数组上。
在乱序的数组中,如何找到第 K 个数呢。要不要先排个序?将数组排好序了,获取下标为 k-1 的数,就是我们要找的数了。不过最快的排序算法的复杂度是 nlogn,不满足题目的要求
该怎么办呢?这里就需要用到之前介绍过的快速排序了
快速排序(Quick Sort)是一种常用的排序算法,其平均时间复杂度为 O(nlogn)。快速排序的主要思想是分治法,通过递归地将数组划分为较小和较大的两个子数组,然后递归地排序两个子数组。
快速排序的实现步骤如下:
- 选择一个基准元素(pivot),通常选择第一个或最后一个元素。
- 将数组划分为两个子数组,一个子数组中的元素都比基准元素小,另一个子数组中的元素都比基准元素大。
- 对这两个子数组递归地应用快速排序算法,直到子数组的大小为1或0,即它们已经有序。
具体算法分析,可以看这篇文章:
快序排序过程中,会使用一个基准元素,将数组划分成左右两个子数组,左边数组中的所有内容都会比基准元素小,右边数组都会比基准元素小。这时候,我们就看看左边数组的长度是否大于 K,如果是,那么第 K 个数肯定在左边的数组中,如果基准元素的下标刚好是 k-1,那么基准元素就是我们要找的,其他情况就是在右边数组里面了。
如果在左边数组里面,我们就在左边数组中再次选取一个基准元素重复上面的动作,直到找到我们要找的
数组下标从 0 开始。对于第 K 个元素,从 1 开始计数
下面看看代码如何实现:🥳每日一练-快速排序-JS简单版
代码实现
javascript
// splitArray 函数用于将数组划分为两个子数组,其中一个子数组中的元素都小于基准元素,另一个子数组中的元素都大于基准元素
const splitArray = (array, left, right) => {
// 选择数组的第一个元素作为基准元素
const temp = array[left];
// 使用双指针从右向左遍历数组,将小于基准元素的元素放到左边,大于基准元素的元素放到右边
while (left < right) {
while (left < right && array[right] > temp) right--;
array[left] = array[right];
while (left < right && array[left] <= temp) left++;
array[right] = array[left];
}
// 将基准元素放到正确的位置
array[left] = temp;
// 返回基准元素的索引
return left;
};
// findKNum 函数用于找到数组中第 k 个小的元素
const findKNum = (array, k, left, right) => {
// 如果数组的长度为 0 或 1,直接返回对应的元素
if (left > right) return null;
// 使用 splitArray 函数将数组划分为两个子数组
const mid = splitArray(array, left, right);
// 计算左子数组的长度
const leftLen = mid - left + 1;
// 如果左子数组的长度等于 k,说明第 k 个元素就是基准元素
if (leftLen == k) return array[mid];
// 如果左子数组的长度大于 k,说明第 k 个元素在左子数组中,递归查找左子数组
if (leftLen > k) return findKNum(array, k, left, mid - 1);
// 如果左子数组的长度小于 k,说明第 k 个元素在右子数组中,递归查找右子数组
return findKNum(array, k - leftLen, mid + 1, right);
};
findKNum 函数用于找到数组中第 k 个小的元素。其中就利用了快速排序的思想。先使用 splitArray 函数将数组划分为两个子数组,然后计算左子数组的长度,如果左子数组的长度等于 k,说明第 k 个元素就是基准元素;如果左子数组的长度大于 k,说明第 k 个元素在左子数组中,递归查找左子数组;如果左子数组的长度小于 k,说明第 k 个元素在右子数组中,递归查找右子数组。
需要注意的是,在右数组中递归查找的时候,需要将 k - leftLen,表示已经找过了前面 leftLen 个数,只要找剩下的第 k - leftLen 就可以了
这篇文章有对这种处理做了详细的解释,可以移步了解: 🥳每日一练-找到数组中最小的k个数-JS简易版
测试代码
javascript
const array = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
const k = findKNum(array, 5, 0, array.length - 1);
console.log(k);
// 50
为了让测试结果更直观,这里将数组设置为有序。代码中想找到第 5 大的数,那么 k 就应该是 50. 如打印的那样
下面测测乱序
javascript
const array = [12, 32, 36, 74, 5, 26, 17, 78, 49, 10];
console.log(findKNum(array, 1, 0, array.length - 1));
console.log(findKNum(array, 2, 0, array.length - 1));
console.log(findKNum(array, 3, 0, array.length - 1));
console.log(findKNum(array, 4, 0, array.length - 1));
console.log(findKNum(array, 5, 0, array.length - 1));
// 5
// 10
// 12
// 17
// 26
这里也为了测试结果更加直观,直接将前 5 个数字打印了出来😄
总结:
这篇文章分享了在乱序数组中找到第 k 个数,主要利用了快速排序的思想,和差分数组之后的长度信息利用,不算难。算是帮大家复习了下快速排序吧哈哈
有什么问题可以评论区留言哦。我每天都会分享一篇算法小练习,喜欢就点赞+关注吧