元素TopK
2024年04月09日
一、最小的 K 个数
输入整数数组 arr
, 找出其中最小的 k
个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
1.1 快排思想
思路: 根据快速排序原理,如果某次哨兵划分后 基准数正好是第 k+1 小的数字 ,那么此时基准数左边的所有数字便是题目所求的 最小的 k 个数 。根据此思路,考虑在每次哨兵划分后,判断基准数在数组中的索引是否等于 k ,若 true 则直接返回此时数组的前 k 个数字即可。
- getLeastNumbers() 函数:
- 若 k 大于数组长度,则直接返回整个数组;
- 执行并返回 quick_sort() 即可;
- quick_sort() 函数:
- 哨兵划分: 划分完毕后,基准数为 arr[i] ,左 / 右子数组区间分别为 [l,i−1], [i+1,r]
- 递归或返回:
- 若 k < i ,代表第 k + 1 小的数字在 左子数组 中,则递归左子数组;
- 若 k > i ,代表第 k + 1 小的数字在 右子数组 中,则递归右子数组;
- 若 k = i ,代表此时 arr[k] 即为第 k + 1 小的数字,则直接返回数组前 k 个数字即可;
function quickSort(array, start, end,k) {
let left = start;
let right = end;
let pivot = array[left];
while (left < right) {
while (left < right && array[right] > pivot) {
right--;
}
while (left < right && array[left] <= pivot) {
left++;
}
[array[left], array[right]] = [array[right], array[left]];
}
[array[start], array[left]] = [array[left], array[start]];
if(left > k){
quickSort(array, start, right - 1,k);
}
if(left < k){
quickSort(array, left + 1, end,k);
}
return array.slice(0,k)
}
function getLeastNumbers(array,k){
if(k === 0 || array.length === 0){
return [];
}
return quickSort(array,0,array.length-1,k);
}
性能分析:
- 时间复杂度:期望为 O(n)
- 空间复杂度:期望为 O(logn)
二、前 K 个高频元素
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前k
高的元素。你可以按任意顺序返回答案。
示例1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例2:
输入: nums = [1], k = 1
输出: [1]
进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
2.1 快排思想
实现
function quickSort(array, start, end, k) {
let left = start;
let right = end;
let pivot = array[left][1];
while (left < right) {
while (left < right && array[right][1] < pivot) {
right--;
}
while (left < right && array[left][1] >= pivot) {
left++;
}
[array[left], array[right]] = [array[right], array[left]];
}
[array[start], array[left]] = [array[left], array[start]];
if (left > k) {
quickSort(array, start, right - 1, k);
}
if (left < k) {
quickSort(array, left + 1, end, k);
}
return array.slice(0, k);
}
function topKFrequent(array, k) {
const map = new Map();
for (let item of array) {
map.set(item, map.has(item) ? map.get(item) + 1 : 1);
}
const arrayOfFrequent = [...map.entries()];
if (k >= arrayOfFrequent.length) {
return [...map.keys()];
}
const result = quickSort(arrayOfFrequent, 0, arrayOfFrequent.length - 1, k);
return [...new Map(result).keys()];
}
三、数组中的第 K 个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
3.1 快排思想
思路: 根据快速排序原理,如果某次哨兵划分后 基准数正好是第 k-1
大的数字 ,那么此时基准数左边的所有数字便是题目所求的 最大的 k
个数 。根据此思路,考虑在每次哨兵划分后,判断基准数在数组中的索引是否等于 k-1
,若 true
则直接返回此时数组的 k-1
元素即可。
-
findKthLargest()
函数:-
若
k
大于数组长度,则直接返回整个数组 -
执行并返回
quick_sort()
即可
-
-
quick_sort()
函数:-
哨兵划分: 划分完毕后,基准数为
arr[i]
,左 / 右子数组区间分别为[l,i−1], [i+1,r]
-
递归或返回:
- 若 k-1 < i ,代表第 k 大的数字在 左子数组 中,则递归左子数组;
- 若 k-1 > i ,代表第 k 大的数字在 右子数组 中,则递归右子数组;
- 若 k-1 = i ,代表此时 arr[k-1] 即为第 k 大的数字,则直接返回数组前 arr[k-1] 元素即可;
-
function quickSort(array, start, end,k) {
let left = start;
let right = end;
let pivot = array[left];
while (left < right) {
while (left < right && array[right] < pivot) {
right--;
}
while (left < right && array[left] >= pivot) {
left++;
}
[array[left], array[right]] = [array[right], array[left]];
}
[array[start], array[left]] = [array[left], array[start]];
if(left > k-1){
quickSort(array, start, right - 1,k);
}
if(left < k-1){
quickSort(array, left + 1, end,k);
}
return array[k-1]
}
function findKthLargest(array,k){
if(k === 0 || array.length === 0){
return [];
}
return quickSort(array,0,array.length-1,k);
}
性能分析:
- 时间复杂度:期望为 O(n)
- 空间复杂度:期望为 O(logn)