1.题目
输入n
个整数,找出其中最小的K
个数。例如输入4,5,1,6,2,7,3,8
这8
个数字,则最小的4
个数字是1,2,3,4
。
2.我的题解
2.1最小堆
使用最小堆实现:
- 实现一个最小堆,借助
vector
,下标从1
开始存储; - 实现删除最小元素的函数
del
,要求删除后保持堆有序,返回删除的元素; - 利用输入数据建堆;
- 连续执行
K
次del
,并将函数返回值添加到输出结果。 - 时间复杂度:
O(n+klogn)
- 空间复杂度:
O(n)
下沉操作构建堆:
- 确定堆的大小,开辟空间并填入数值;
- 从第一个非叶节点开始做下沉操作,直到第一个节点。
构建堆的复杂度计算: 以n
个节点,树高h
的满二叉树(堆)为例,显然h=log(n+1)
;
各层节点关系: 设想一个满二叉树,下一层的节点数总是翻倍,而总结点个数约等于最后一层节点个数的两倍(实际上就少1);故每一层的节点数和都可以由n
表示。
堆的层数 | 下沉时比较次数 | 最大节点数 | 最大总操作数 |
---|---|---|---|
1 | h | 1/2h * (n+1) | h/2h * (n+1) |
2 | h-1 | 1/2h-1 * (n+1) | (h-1)/2h-1 * (n+1) |
3 | h-2 | 1/2h-2 * (n+1) | (h-2)/2h-2 * (n+1) |
…… | …… | …… | …… |
k | h-k | 1/2h+1-k * (n+1) | (h-k)/2h+1-k * (n+1) |
…… | …… | …… | …… |
h | 1 | 1/2 * (n+1) | 1/2 * (n+1) |
因此T=O(n)
。
class Solution {
vector<int> vec;//小顶堆
int N = 0;
void swap(int * a, int * b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
void sink(int k) {
while ((k<<1) <= N) {
int j = k << 1;
if (j+1<=N && vec[j] > vec[j + 1])j++;
if (vec[k] > vec[j])swap(&vec[k], &vec[j]);
k = j;
}
}
int del() {
swap(&vec[1], &vec[N--]);
sink(1);
return vec[N+1];
}
void makeHeap(vector<int> &data) {
N = data.size();
vec.resize(N << 1);
for(int i=0;i<data.size();i++)vec[i+1]=data[i];
for(int i=N/2;i>=1;i--)
sink(i);
}
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> res;
if (k == 0 || input.empty() || k>input.size())return res;
//make heap
makeHeap(input);
//find
while (k-- > 0) {
int tmp = del();
//cout << tmp << " ";
res.push_back(tmp);
}
return res;
}
};
3.别人的题解
3.1 大顶堆
- 时间复杂度:
O(nlogk)
- 空间复杂度:
O(k)
3.2 partition思想
- 时间复杂度:
O(n)~O(n^2)
- 空间复杂度:
O(1)
4.总结与反思
(1)Markdown
数学公式。
(2)partition
思想。
(3)大顶堆、小顶堆。适合处理海量数据。
来源:CSDN
作者:永封
链接:https://blog.csdn.net/weixin_43951240/article/details/104130226