美文网首页数据结构&算法&人工智能
剑指offer编程题—最小的K个数

剑指offer编程题—最小的K个数

作者: 零岁的我 | 来源:发表于2020-03-18 16:08 被阅读0次

题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

思路整理
很简单的一题。基本思想就是排序,正好复习一下常用的排序算法。

  1. 快速排序
    1)基本思想:快速排序是对冒泡排序的一种改进。其基本思想就是通过一趟排序将待排序序列分割为独立的两部分,其中一部分记录的关键字都比另一部分记录的关键字小,再分别对这两部分使用快排,直到整个序列有序为止。
    2)时间复杂度:O(nlogn).
    3)快速排序在所有时间复杂度为O(nlogn)的排序算法中,性能是最好的;
    4)但是快排需要一个栈的辅助空间;
    5)在初始待排序序列有序或基本有序的情况下,快排退化为冒泡排序,此时不适合用快排。
class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if(input.size()<k || k<=0) return vector<int>();
        quickSort(input,k);
        vector<int> res(input.begin(),input.begin()+k);
        return res;
    }
    void quickSort(vector<int> &input,int k){
        Qsort(input,0,input.size()-1,k);
    }
    void Qsort(vector<int> &input,int low,int high,int k){
        if(low<high){
            int vipot=Partition(input,low,high);
            if(vipot>=k-1) Qsort(input,low,vipot-1,k);  //如果左边记录已经达到k个,则只用对左边元素进行排序,不用管右边较大的记录
            else{
                Qsort(input,low,vipot-1,k);
                Qsort(input,vipot+1,high,k);
            }
        }
    }
    int Partition(vector<int> &input,int low,int high){
        int tmp=input[low];
        while(low<high){
            while(low<high && tmp<=input[high]) --high;
            input[low]=input[high];
            while(low<high && tmp>=input[low]) ++low;
            input[high]=input[low];
        }
        input[low]=tmp;
        return tmp;
    }
};
  1. 堆排序
    1)基本思想:通过将无序序列转换为堆,可以直接获得最小值或最大值,取出堆顶元素(最小值或最大值),令剩余的记录重新构建成堆,可以获得次小值或次大值,如此反复,可以获得一个有序序列。
    2)时间复杂度:相比快速排序,堆排序在最坏的情况其时间复杂度也是O(nlogn);
    3)堆排序只需要一个辅助记录空间;
    4)堆排序的时间主要耗费在建初堆和反复调整堆的筛选上;
    5)对于记录数比较少的序列,不适合用堆排序。
class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if(input.size()<k || k<=0) return vector<int>();
        vector<int> res;
        HeapSort(input,res,k);
        return res;
    }
    void HeapSort(vector<int> &input,vector<int> &res,int k) {
        int i;
        for(i=input.size()/2;i>=0;--i){
            HeapAdjust(input,i,input.size()-1);
        }
        int tmp;
        for(i=input.size()-1;i>=0;--i){
            res.push_back(input[0]); //每次将堆顶的最小记录放入结果集
            if(res.size()==k) break; //获取到最小的k个记录后退出排序算法,避免超时
            tmp=input[i];
            input[i]=input[0];
            input[0]=tmp;
            HeapAdjust(input,0,i-1);
        }
    }
    void HeapAdjust(vector<int> &input,int s,int m){ //筛选堆使其为最小堆
        int tmp=input[s];
        int i;
        for(i=2*s+1;i<=m;i=2*i+1){
            if(i+1<=m && input[i+1]<input[i]) ++i;
            if(input[i]>=tmp) break;
            input[s]=input[i];
            s=i;
        }
        input[s]=tmp;
    }
};
  1. 归并排序
    1)基本思想:归并排序的基本思想就是将待排序序列分割为两个独立的部分,再依次对分的的两部分进行归并排序,之后再将二者合并;
    2)时间复杂度:无论再最好情况下还是在最坏情况下,归并排序的时间复杂度都是O(nlogn);
    3)归并排序是一种稳定的排序算法;
    4)空间复杂度:O(n).显然归并排序不是就地排序。
class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if(input.size()<k || k<=0) return vector<int>();
        MergeSort(input,0,input.size()-1,k);
        vector<int> res(input.begin(),input.begin()+k);
        return res;
    }
    void MergeSort(vector<int> &input,int low,int high,int k){
        if(low<high){
            int mid=(low+high)/2;
            MergeSort(input,low,mid,k);
            MergeSort(input,mid+1,high,k);
            Merge(input,low,mid,high);
        }
    }
    void Merge(vector<int> &input,int low,int mid,int high){
        vector<int> res;
        int i=low;
        int j=mid+1;
        while(i<=mid && j<=high){
            if(input[i]<=input[j]){
                res.push_back(input[i]);
                ++i;
            }
            else{
                res.push_back(input[j]);
                ++j;
            }
        }
        while(i<=mid){
            res.push_back(input[i]);
            ++i;
        }
        while(j<=high){
            res.push_back(input[j]);
            ++j;
        }
        for(i=0;i<res.size();++i){
            input[low+i]=res[i];
        }
    }
};
  1. 优先级队列
    优先级队列内部也是使用的堆排序技术,默认是构建大根堆,也就是优先级队列能始终保证队头元素最大,但是其内部不一定是完全有序的。
class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if(input.size()<k || k<=0) return vector<int>();
        priority_queue<int,vector<int>,less<int> > q;
        int i=0;
        while(i<input.size()){
            q.push(input[i]);
            if(q.size()>k) q.pop();
            ++i;
        }
        vector<int> res;
        while(!q.empty()){
            res.push_back(q.top());
            q.pop();
        }
        return res;
    }
};

相关文章

网友评论

    本文标题:剑指offer编程题—最小的K个数

    本文链接:https://www.haomeiwen.com/subject/htmqyhtx.html