28 最小的K个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
 
思路:
解法1:对于小规模数据,可以采用类似前题的快速排序思路,pivot之前的点都是比它小的,之后的点都是比它大的。不管是找中位数还是找前k大,前k小,都可以使用这个方法,平均复杂度是O(N),但是最坏时间复杂度是O(N*N)。这样得到最后k个数是没有进行排序的,所以降低了时间复杂度。
这里需要注意idx初值不该设为0,因为找最小的0个数的时候idx = lo = 0;就不会进入循环当中,所以应该注意39行和50行。这题有个大坑就是k的讨论,k可能小于等于0,也可能大于size,所以必须要进行讨论。
这种方法会对输入的数组进行改变,所以应该要明确告诉面试官这样是否允许。而且这种做法适合小规模数据。
 
 1 class Solution {
 2 public:
 3     int partition(vector<int> &input,int lo,int hi){
 4         int pos = lo + rand() % (hi - lo + 1);
 5         int pivot = input[pos];
 6         swap(input[lo],input[pos]);
 7         while(lo < hi){
 8             while(lo < hi){
 9                 if(input[hi] > pivot){
10                     --hi;
11                 }
12                 else{
13                     input[lo] = input[hi];
14                     ++lo;
15                     break;
16                 }
17             }
18             while(lo < hi){
19                 if(input[lo] < pivot){
20                     ++lo;
21                 }
22                 else{
23                     input[hi] = input[lo];
24                     --hi;
25                     break;
26                 }
27             }
28         }
29         input[lo] = pivot;
30         return lo;
31     }
32     vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
33         if(input.empty() || k > input.size() || k <= 0){
34             return {};
35         }
36         vector<int> result;
37         int lo = 0,hi = input.size() - 1;
38         int idx ;
39         idx = partition(input,lo,hi);//
40         int target = k - 1;
41         while(idx != target){            
42             
43             if(idx < target){
44                 lo = idx + 1;
45                 
46             }
47             else if(idx > target){
48                 hi = idx - 1;
49             }
50             idx = partition(input,lo,hi);
51         }
52         for(int i = 0;i < k;++i){
53             result.push_back(input[i]);
54         }
55         return result;
56     }
57 };

第二种方法:

解法2:O(nlogk)的算法,特别适合处理海量数据。

priority_queue加入和删除操作都是O(logk),top操作是O(1)。默认top的是最大元素。优先级队列本质是基于vector实现的最大堆,因为vector通过下标访问数组,所以top操作复杂度是O(1)的;

STL的set和map还有就是multise和multimap都是基于红黑树实现的,begin是最小元素,即基于最小堆实现的,只有自己写一个仿函数就可以得到最大堆,注意就重载()就行了,stack和queue都是基于deque。红黑树中查找、删除和插入操作的时间复杂度都是O(logk)。

如果要使用仿函数要引入头文件functional。才可以使用greater<int>。

但它是如何保证一棵n个结点的红黑树的高度始终保持在logn的呢?这就引出了红黑树的5个性质:

  1. 每个结点要么是红的要么是黑的。  
  2. 根结点是黑的。  
  3. 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。  
  4. 如果一个结点是红的,那么它的两个儿子都是黑的。  
  5.  对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。
#include<iostream>
#include<vector>
#include<string>
#include <cstdio>
#include<queue>
#include<algorithm>
#include<set>
using namespace std;
template<typename T>
struct greater{
    bool operator() (const T &a, const T &b) {
        return a > b;
    }
};

int main() {    
    multiset<int,greater<int>> hashSet;    
    for (int i = 0; i < 4; ++i) {//输入1,3,55,4
        int a = 0;
        cin >> a;
        hashSet.insert(a);        
    }
    multiset<int>::iterator iter = hashSet.begin();//得到55
    cout << *iter << endl;
    system("pause");
}
仿函数最大set
#include<iostream>
#include<vector>
#include<set>
#include<functional>
using namespace std;
int main() {    
    multiset<int,greater<int>> hashSet;    
    for (int i = 0; i < 4; ++i) {//输入1,3,55,4
        int a = 0;
        cin >> a;
        hashSet.insert(a);        
    }
    multiset<int>::iterator iter = hashSet.begin();//得到55
    cout << *iter << endl;
    system("pause");
}
使用functional
class Solution {
public:   
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> result;
        if(input.empty() || k <= 0 || k > input.size()){
            return result;
        }
        multiset<int,greater<int>> hashSet;
        multiset<int,greater<int>>::iterator iter = hashSet.begin();
        for(int i = 0;i < input.size();++i){
            iter = hashSet.begin();
            if(hashSet.size() < k){
                hashSet.insert(input[i]);
            }
            else{
                if(*iter > input[i]){
                    hashSet.erase(*iter);
                    hashSet.insert(input[i]);
                }
            }
        }
        iter = hashSet.begin();
        for(iter;iter != hashSet.end();++iter){
            result.push_back(*iter);
        }
        return result;
    }
};

解法3:

使用priority_queue实现最大堆。(默认是最大堆,即top等于最大的元素,降序);

底层容器是vector实现的最大堆(stl源码)

模板:priority_queue<int,vector<int>,greater<int>>,升序,最小堆

class Solution {
public:   
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> result;
        if(input.empty() || k <= 0 || k > input.size()){
            return result;
        }
        priority_queue<int> q;
        for(int i = 0;i < input.size();++i){
            if(q.size() < k){
                q.push(input[i]);
            }
            else{
                if(q.top() > input[i]){
                    q.pop();
                    q.push(input[i]);
                }
            }
        }
        for(int i = 0;i < k;++i){
            result.push_back(q.top());
            q.pop();
        }
        return result;
    }
};
原文地址:https://www.cnblogs.com/dingxiaoqiang/p/8031843.html