leetcode 链接:最小的k个数
题目
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入 4、5、1、6、2、7、3、8 这 8 个数字,则最小的 4个数字是 1、2、3、4。
示例:
输入:arr = [3,2,1], k = 2输出:[1,2] 或者 [2,1]
输入:arr = [0,1,2,1], k = 1
输出:[0]
解答 & 代码
解法一:堆排序(小根堆)
建立小根堆后,进行 k 次交换调整,数组的最后 k 个元素就是最小的 k 个数(不用进行完整的排序)
class Solution {
private:
void adjustHeap(vector<int>& arr, int start, int end)
{
int dadIdx = start;
int sonIdx = start * 2 + 1;
while(sonIdx <= end)
{
if(sonIdx + 1 <= end && arr[sonIdx + 1] < arr[sonIdx])
sonIdx = sonIdx + 1;
if(arr[sonIdx] < arr[dadIdx])
{
swap(arr[sonIdx], arr[dadIdx]);
dadIdx = sonIdx;
sonIdx = dadIdx * 2 + 1;
}
else
return;
}
}
void minHeap(vector<int>& arr, int k)
{
int len = arr.size();
for(int i = len / 2 - 1; i >= 0; --i)
adjustHeap(arr, i, len - 1);
swap(arr[0], arr[len - 1]);
for(int i = 1; i < k; ++i)
{
adjustHeap(arr, 0, len - i - 1);
swap(arr[0], arr[len - i - 1]);
}
}
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
int len = arr.size();
minHeap(arr, k);
vector<int> result(k, 0);
for(int i = 0; i < k; ++i)
result[i] = arr[len - i -1];
return result;
}
};
执行结果:
执行结果:通过
执行用时:32 ms, 在所有 C++ 提交中击败了 71.73% 的用户
内存消耗:18.3 MB, 在所有 C++ 提交中击败了 79.10% 的用户
解法二:堆排序(大根堆)
解法一建立小根堆,需要对数组中所有元素建立堆,再取 k 次堆顶,得到 k 个最小的数。
实际上,一般取最小的 k 个数使用大根堆,只需要先将数组的前 k 个数建立大根堆,再依次将数组中剩余元素与大根堆堆顶元素比较,如果小于堆顶元素,则与堆顶交换,并调整大根堆。最终前 k 个数就是最小的 k 个数
class Solution {
private:
void adjustHeap(vector<int>& arr, int start, int end)
{
int dadIdx = start;
int sonIdx = start * 2 + 1;
while(sonIdx <= end)
{
if(sonIdx + 1 <= end && arr[sonIdx + 1] > arr[sonIdx])
sonIdx = sonIdx + 1;
if(arr[sonIdx] > arr[dadIdx])
{
swap(arr[sonIdx], arr[dadIdx]);
dadIdx = sonIdx;
sonIdx = dadIdx * 2 + 1;
}
else
return;
}
}
void maxHeap(vector<int>& arr, int k)
{
int len = arr.size();
for(int i = k / 2 - 1; i >= 0; --i)
adjustHeap(arr, i, k - 1);
}
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
int len = arr.size();
maxHeap(arr, k); // 将前 k 个数建立大根堆
for(int i = k; i < len; ++i) // 将剩下的数依次与堆顶元素比较
{
if(arr[i] < arr[0]) // 如果当前元素的值小于堆顶元素
{
swap(arr[0], arr[i]); // 则将当前元素与堆顶元素交换
adjustHeap(arr, 0, k - 1); // 调整堆
}
}
vector<int> result(k, 0);
for(int i = 0; i < k; ++i) // 最终前 k 个数就是最小的 k 个数
result[i] = arr[i];
return result;
}
};
执行结果:
执行结果:通过
执行用时:24 ms, 在所有 C++ 提交中击败了 95.06% 的用户
内存消耗:18.3 MB, 在所有 C++ 提交中击败了 71.13% 的用户
解法三:快排
快排,不用完整排序,如果分割点 pivot == k - 1,说明当前数组前面 k 个就是最小的 k 个数,虽然这 k 个数的顺序不能保证,但也符合题目要求
class Solution {
private:
int partition(vector<int>& arr, int start, int end)
{
int randomIdx = rand() % (end - start + 1) + start;
swap(arr[start], arr[randomIdx]);
int pivot = arr[start];
int left = start;
int right = end;
while(left < right)
{
while(left < right && arr[right] >= pivot)
--right;
arr[left] = arr[right];
while(left < right && arr[left] <= pivot)
++left;
arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}
void quickSort(vector<int>& arr, int start, int end, int k)
{
if(start >= end)
return;
int pivot = partition(arr, start, end);
if(pivot == k - 1)
return;
quickSort(arr, start, pivot - 1, k);
quickSort(arr, pivot + 1, end, k);
}
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
int len = arr.size();
quickSort(arr, 0, len - 1, k);
vector<int> result(k, 0);
for(int i = 0; i < k; ++i)
result[i] = arr[i];
return result;
}
};
执行结果:
执行结果:通过
执行用时:36 ms, 在所有 C++ 提交中击败了 54.60% 的用户
内存消耗:18.3 MB, 在所有 C++ 提交中击败了 69.54% 的用户
