leetcode链接
题目:
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
解题:
- 方法一、最小堆排序:适用于数据流及大数据量的场景
首先、维护一个k大小的最小堆。从索引1开始到k,逐个与他们的父亲比较。链接
其次、对每个数据与堆顶的数据进行比较,如果大于堆顶的就替换它。
最后、进行下沉排序。
说一个概念:二叉堆和二叉树是由区别的。
二叉堆用数组构建;二叉树用链表构建。
二叉堆儿子比父亲小;二叉树右儿子最大。
注意:为什么要先构建上浮,再下沉。而不是上浮,上浮。那是因为第一次构建的时候,数组是无序的,上浮只适用于无序数组。当上浮完成后,此时数组是有序的小根堆,那么根被替换之后,利用下沉重排会快很多,否则上浮耗时。
bool cmp(int a, int b) { return a > b; }
void down(vector<int>& nums,int i, int n) {
while (2 * i + 1 <= n) {
int j = 2 * i + 1;
if (j + 1 <= n && cmp(nums[j], nums[j + 1])) {
j++;
}
if (nums[i] > nums[j]) {
swap(nums[i], nums[j]);
i = j;
}
else {
break;
}
}
}
int makeMinHeap(vector<int>& nums,int k) {
// 构建大小为k容量的小顶堆
for (int i = 0; i < k; i++) {
int m = i;
while (m > 0&& cmp(nums[m/2], nums[m])) {
swap(nums[m/2], nums[m]);
m = m / 2;
}
}
//开始加入新的值进行比较
for (int i = k; i < nums.size(); i++) {
if (nums[i] > nums[0]) {
swap(nums[i], nums[0]);
// 下沉
down(nums, 0, k - 1);
}
}
return nums[0];
}
- 方法二、改进的快速排序:适用于已知小数据量的场景
常规快排时间复杂度O(nlogn),而这个是O(n)
int quickSort(vector<int>& nums, int start,int end,int k) {
if (start > end) {
// 若k越界则会出现这种情况,返回-1;
return -1;
}
if (start == end) {
// 由于start <= k, 且k <= end, 若start == end, 则三者相等,找到目标值
return nums[start];
}
// 设枢纽元为数组末尾元素
int pivot = nums[end];
// l为待交换元素下标
int l = start;
for (int i = start; i < end; i++) {
if (nums[i] > pivot) {
// 寻找大于枢纽元的值,将大于枢纽元的值与nums[l]交换
swap(nums[i], nums[l]);
l++;
}
}
// 经过一番搜索,从下标为 start 到下标 l-1 均为大于枢纽元的值,再将枢纽元与nums[l]交换,则nums[l]即为第 (l + 1)大的值
swap(nums[end], nums[l]);
if (k == l) {
return nums[l];
}
else if (k > l) {
// 后半段进行递归
return quickSort(nums, l + 1, end, k);
}
else {
// 前半段进行递归
return quickSort(nums, start, l - 1, k);
}
}
int findKthLargest(vector<int>& nums, int k) {
return quickSort(nums, 0, nums.size() - 1, k - 1);
}