第k小的数

来源:互联网 发布:勒布朗詹姆斯 知乎 编辑:程序博客网 时间:2024/05/21 07:07

输入n个整数和一个正整数k(1<=k<=n),输出这些整数从小到大排序后的第k个

思路1:最容易想到的方法:先对这个序列从小到大排序,然后输出前面的最小的k个数即可。如果选择快速排序法来进行排序,则时间复杂度:O(n*logn)

class Solution  //时间复杂度O(NlogN){    public:        int numkth(vector<int>& number,int k)        {            sort(number.begin(),number.end());            return number[k-1];        }};

思路2:在思路1的基础上更进一步想想,题目并没有要求要查找的k个数,甚至后n-k个数是有序的,既然如此,咱们又何必对所有的n个数都进行排序列?如此,我们能想打的一个方法是:遍历n个数,先把最先遍历到得k个数存入大小为k的数组之中,对这k个数,利用选择或交换排序,找到k个数中的最大数kmax(kmax设为k个元素的数组中最大元素),用时O(k)(你应该知道,插入或选择排序查找操作需要O(k)的时间),后再继续遍历后n-k个数,x与kmax比较:如果x< kmax,则x代替kmax,并再次重新找出k个元素的数组中最大元素kmax‘;如果x>kmax,则不更新数组。这样,每次更新或不更新数组的所用的时间为O(k)或O(0),整趟下来,总的时间复杂度平均下来为:n*O(k)=O(n*k)

class Solution{    public:        int numkth(vector<int>& number,int k)//时间复杂度O(N*k)        {            int *num=new int[k];            for(int i=0;i<k;i++)                num[i]=number[i];            for(int j=k;j<number.size()-k;j++)            {                if(number[j]<num[indexkth(num,k)])                    num[indexkth(num,k)]=number[j];            }            int result=num[indexkth(num,k)];            delete []num;            return result;        }        int indexkth(int num[],int k)//找到k个数中最大值的索引下标O(k)        {            int index=0;            int max=num[index];            for(int i=1;i<k;i++)            {                if(num[i]>max)                {                    index=i;                    max=num[i];                }            }            return index;        }};

思路3:与思路2方法类似,只是用容量为k的最大堆取代思路2中数组的作用(从数组中找最大数需要O(k)次查找,而从更新一个堆使之成为最大堆只需要O(logk)次操作)。具体做法如下:用容量为k的最大堆存储最先遍历到的k个数,并假设它们即是最小的k个数,建堆费时O(k)后,有k1< k2< …< kmax(kmax设为大顶堆中最大元素)。继续遍历数列,每次遍历一个元素x,与堆顶元素比较,x< kmax,更新堆(用时logk),否则不更新堆。这样下来,总费时O(k+(n-k)*logk)=O(n*logk)。

class Solution{    public:        int numkth(vector<int>& number,int k)//总费时O(k+(n-k)*logk)=O(n*logk)        {            int *num=new int[k],i;            for(i=0;i<k;i++)                num[i]=number[i];            for(i=(k-1)/2;i>=0;i--)//建堆O(k)                Adjust(num,i,k);            for(i=k;i<number.size();i++)            {                if(number[i]<num[0])                {                    num[0]=number[i];                    Adjust(num,0,k);                }            }            return num[0];        }        void Adjust(int A[],int i,int N)//时间复杂度O(logN)        {            int temp,child;            for(temp=A[i];2*i+1<N;i=child)            {                child=2*i+1;                if((child!=N-1)&&A[child+1]>A[child])                    child++;                if(temp<A[child])                    A[i]=A[child];                else                    break;            }            A[i]=temp;        }};

思路4:按编程之美中给出的描述,类似快速排序的划分方法,N个数存储在数组S中,再从数组中随机选取一个数X(随机选取枢纽元,可做到线性期望时间O(N)的复杂度),把数组划分为Sa和Sb俩部分,Sa<=X<=Sb,如果要查找的k个元素小于Sa的元素个数,则返回Sa中较小的k个元素,否则返回Sa中所有元素+Sb中小的k-|Sa|个元素。像上述过程一样,这个运用类似快速排序的partition的快速选择SELECT算法寻找最小的k个元素,在最坏情况下亦能做到O(N)的复杂度。

class Solution{    public:        int numkth(vector<int>& number,int k)//分治法,时间复杂度为O(N)        {            int num=number.size();            int *A=new int[num];            for(int i=0;i<num;i++)                A[i]=number[i];            int result=helper(A,0,num-1,k);            delete []A;            return result;        }        int helper(int A[],int low,int high,int k)        {            int pivot=A[low];            int L=low,R=high;            while(low<high)            {                while(low<high&&A[high]>pivot)                    high--;                A[low]=A[high];                while(low<high&&A[low]<=pivot)                    low++;                A[high]=A[low];            }            A[low]=pivot;            if(low==k-1)                return A[low];            else if(low>k-1)                return helper(A,L,low-1,k);            else                return helper(A,low+1,R,k);        }};

思路5:仍然用到数据结构:堆。具体做法为:针对整个数组序列建最小堆,建堆所用时间为O(n),然后取堆中的前k个数,总的时间复杂度即为:O(n+k*logn)。

class Solution{    public:        int numkth(vector<int>& number,int k)//总费时O(k+(n-k)*logk)=O(n*logk)        {            int length=number.size();            int *num=new int[length],i,temp;            for(i=0;i<length;i++)                num[i]=number[i];            for(i=(length-1)/2;i>=0;i--)//建最小堆O(N)                Adjust(num,i,N);            for(i=length-1;i>=length-k;i--)            {                temp=num[0];num[0]=num[i];num[i]=temp;                Adjust(num,0,i);            }            return num[length-k];        }        void Adjust(int A[],int i,int N)//时间复杂度O(logN)        {            int temp,child;            for(temp=A[i];2*i+1<N;i=child)            {                child=2*i+1;                if((child!=N-1)&&A[child+1]<A[child])                    child++;                if(temp>A[child])                    A[i]=A[child];                else                    break;            }            A[i]=temp;        }}; 
0 0