第40题:最小的K个数
题目描述:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
示例1
输入:[4,5,1,6,2,7,3,8],4
返回值:[1,2,3,4]
实现思路
方法一:直接采用排序的方式,调用STL中sort
方法
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> ret;
if (k==0 || k>input.size()) return ret;
sort(input.begin(), input.end());
return vector<int>(input.begin(), input.begin()+k);
}
};
时间复杂度:O(nlongn)
空间复杂度:O(1)
方法二:利用快速排序的方法实现。
实现原理:基于数组的第K个数字来调整,使得比K个数字小的所有数字都位于数组的左边,比第K个数字大的所有数字都位于数组的右边,经过这样的调整后,位于数组中左边的K个数字就是最小的K个数字。
class Solution {
public:
int partition(vector<int>& array, int low, int high)
{
int piv = array[low];
while (low < high) {
while ((low < high) && (array[high] >= piv)) {
high--;
}
array[low] = array[high];
while ((low < high) && (array[low] <= piv)) {
low++;
}
array[high] = array[low];
}
array[low] = piv;
return low;
}
void quickSort(vector<int>& input, int low, int high, int k)
{
int pivot = partition(input, low, high);
if (pivot == k) {
return;
}
else if (pivot > k) {
quickSort(input, 0, pivot-1, k);
}
else {
quickSort(input, pivot+1, high, k);
}
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
vector<int> ret;
if (k == 0 || k > input.size()) {
return ret;
}
int low = 0;
int high = input.size() - 1;
quickSort(input, low, high, k);
for (int i=0; i<k; i++){
ret.push_back(input[i]);
}
return ret;
}
};
时间复杂度为: O ( n ) \Omicron(n) O(n) 每次partition的大小为n+n/2+n/4+... = 2n
,最坏时间复杂度为 O(n^2)
, 因为每次partition都只减少一个元素
空间复杂度: O ( 1 ) \Omicron(1) O(1)
方法三:最大堆实现利用
实现原理:先把前 k 个数据建立一个大根堆,然后对后面的 length-k
个数依次遍历,如果当前数值小于堆顶的值时,则替换堆顶的值,然后对大根堆做向下调整。
class Solution {
public:
void adjustHeap(vector<int>& input, int i, int length)
{
int left = 2 * i + 1; // i节点的左孩子,i从 0 开始
int right = 2 * i + 2; // i节点的右孩子
int max = i; // 先设置父节点和子节点三个节点中最大值的位置为父节点下标
if (left < length && input[left] > input[max]) {
max = left;
}
if (right < length && input[right] > input[max]) {
max = right;
}
//最大值不是父节点,则进行交换
if (max != i) {
int temp;
temp = input[i];
input[i] = input[max];
input[max] = temp;
adjustHeap(input, max, length); //递归调用,保证子树也是最大堆
}
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
if (input.empty() || k == 0 || k > input.size()) {
return vector<int>{};
}
vector<int> result;
//建立堆
for (int i = input.size() / 2 - 1; i >= 0; i--) {
adjustHeap(input, i, k); //堆的大小为k
}
//将后面的数依次和K个数的最大值比较
for (int i = k; i < input.size(); i++) {
if (input[0] > input[i]) {
int temp = input[i];
input[i] = input[0];
input[0] = temp;
adjustHeap(input, 0, k);
}
}
for (int i = 0; i < k; i++) {
result.push_back(input[i]);
}
return result;
}
};
时间复杂度:O(nlongk), 插入容量为k的大根堆时间复杂度为O(longk), 一共遍历n个元素
空间复杂度:O(k)
方法比较
当K数值比较小时,可采用快速排序;当原数组中的数据顺序不可修改,且K数值很大时(海量数据),采用快速排序可能会占满内存,效率不高。此时使用最大堆数据结构实现最好。