【剑指offer】54.最小的K个数
总目录:
1.问题描述
给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。
数据范围:0≤k,n≤10000,数组中每个数的大小0≤val≤1000
要求:空间复杂度 O(n) ,时间复杂度 O(nlogk)
2.问题分析
1堆排序
不需要手撕堆排序,用优先队列priority_queue
2快速排序
快速排序,利用快速排序在基准值左侧是有序序列的特性、且其时间复杂度能满足要求。
快速排序的时间复杂度满足要求,空间复杂度为O(1)
3.代码实例
堆排序
1 class Solution { 2 public: 3 vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { 4 vector<int> ret; 5 if (k==0 || k > input.size()) return ret; 6 7 //使用优先队列 8 priority_queue<int, vector<int>> pq;//默认大优先 9 for (const int val : input) { 10 //先插入k个,然后后续选较小值 11 if (pq.size() < k) { 12 pq.push(val); 13 } 14 else { 15 if (val < pq.top()) { 16 pq.pop(); 17 pq.push(val); 18 } 19 20 } 21 } 22 23 while (!pq.empty()) { 24 ret.insert(ret.begin(),pq.top());//因为大优先 25 pq.pop(); 26 } 27 return ret; 28 } 29 };
快速排序
1 class Solution { 2 public: 3 int partition(vector<int>& input, int left, int right) { 4 int pivot = input[right];//选最后一个元素作基准 5 int mid = left;//大小分界线,从最左边开始 6 7 //使用cur指针遍历剩余元素 8 for (int cur = left; cur < right; cur++) { 9 //如果cur指向的值小于基准值,则应将其放在分界线左侧,而此时分界线指向的值是大于等于基准值的 10 if (input[cur] < pivot) { 11 swap(input[mid], 12 input[cur]);//mid指向的是大于等于基准值的,cur指向的是小于基准值的 13 mid++;//分界线右移动 14 } 15 } 16 17 //将基准值放到分界线处,将分界线处大于基准值的值放到最后 18 swap(input[mid], input[right]); 19 return mid; 20 } 21 22 vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { 23 vector<int> ret; 24 if (k==0 || k > input.size()) return ret; 25 int left = 0, right = input.size(); 26 while (left < right) { 27 int mid = partition(input, left, right-1); 28 //如果已经足够k个有序数字 29 if (mid+1 == k) { 30 return vector<int>({input.begin(), input.begin()+k}); 31 } 32 33 //排左边还是右边 34 if (mid+1 < k) { 35 left = mid + 1; 36 } 37 else { 38 right = mid; 39 } 40 41 } 42 return ret; 43 } 44 };