寻找最小的k个数

来源:互联网 发布:app推广 aso优化平台 编辑:程序博客网 时间:2024/06/10 01:32

问题描述:给定n个整数,求出其中最小的k个数。

分析:这是一个经典的问题了,存在多种解法,各个解法的效率不一样,这里我列举四种常见的解法。

         解法一:可以将所有的数进行排序,然后直接输出前k个数即可。排序算法有很多,读者可以自己选择,如快速排序。

         解法二:利用容器(可以是数组,集合等)实现,

                    我们可以先遍历k个数存入到大小为k的数组中,然后假设这k个数就是最小的k个数;

                    对这k个数,利用选择或交换排序找到这k个元素中的最大值kmax(找最大值需要遍历这k个数,时间复杂度为O(k));

                    继续遍历剩余n-k个数。假设每一次遍历到的新的元素的值为x,把x与kmax比较: 如果x<kmax ,用x替换kmax,并回到上一步重新找出k个元素的数组中最大元素kmax;如果x>kmax,则继续遍历不更新数组。

                    每次遍历,更新或不更新数组的所用的时间为O(k)或O(0)。故整趟下来,时间复杂度为n*O(k)=O(nk)。

         解法三:利用堆结构实现:其实这个方法与解法二相似,只不过这里的容器采用堆这个数据结构,之所以用堆是因为建k个节点的初始堆的时间复杂度为O(k),每维护一次的代价为O(logk),这一步比数组的时间复杂度低。

                   至于后面的都一样,因此这种方法的总时间复杂度为O(nlogk)。

                   这里我给出详细的Java代码,写法比较通用,读者可以很容易的转换为其他语言实现(代码中下标从1开始存数,下标0忽略)。

import java.util.Scanner;public class Qinkxiao {    public static void heapadjust(int H[],int s,int m){   //调整堆        int j,k;        int rc=H[s];        for( j=2*s;j<=m;j*=2)        {            if(j<m && H[j]<H[j+1])++j;            if(rc>=H[j])break;            H[s]=H[j];            s=j;        }            H[s]=rc;            }    public static void createdui(int array[],int k){    //建初始堆        for(int i=k/2;i>=1;--i)            heapadjust(array,i,k);    }    public static void main(String[] args) {            int array[]={4,5,9,2,1,5,3,-5,20};          int n=array.length-1;          int k=3;          System.out.print("n个整数为:");          for(int i=1;i<=n;i++)          {                System.out.print(array[i]+",");           }              System.out.println();              createdui(array,k);                //建k个节点的初始堆              for(int i=k+1;i<=n;i++)            //依次遍历后面的n-k个数                  if(array[i]<array[1])                      {                      array[1]=array[i];                      heapadjust(array,1,k);                      }              System.out.print("前"+k+"小个整数为:");                  for(int i=1;i<=k;i++)                  System.out.print(array[i]+",");    }}

输出结果为:

n个整数为:5,9,2,1,5,3,-5,20,
前3小个整数为:2,1,-5,

          解法四:借助快速排序的思想:利用快速排序中的分割函数。可以证明这种方法的时间度为O(n).

                   具体的Java代码如下:

import java.util.Scanner;public class Qinkxiao {      public static int partition(int array[],int low,int high){         //快速排序中的划分方法        int q=array[low];        int m;        while(low<high){            while(low<high && array[high]>q)high--;            m=array[low];array[low]=array[high];array[high]=m;            while(low<high && array[low]<q)low++;            m=array[low];array[low]=array[high];array[high]=m;        }        return low;    }    public static void main(String[] args) {            int array[]={4,5,9,2,1,5,3,-5,20};          int n=array.length;          int k=3;          System.out.print("n个整数为:");          for(int i=0;i<n;i++)          {                System.out.print(array[i]+",");           }              System.out.println();                           int start=0,end=n-1;              int index=partition(array,start,end);              while(index!=k-1)                           //判断索引位置石佛符合要求              {                  if(index>k-1 )                  { end=index-1;                      index=partition(array,start,end);                  }                  else                   { start=index+1;                      index=partition(array,start,end);                  }              }                            System.out.print("前"+k+"小个整数为:");                  for(int i=0;i<k;i++)                  System.out.print(array[i]+",");    }}

输出结果为:

n个整数为:4,5,9,2,1,5,3,-5,20,
前3小个整数为:-5,1,2,

              上述四种解法比较:

                    解法一:思想最简单,就是将数据排序,直接输出前k个数,编程简单,但是时间复杂度较高,适合数据量少的场合。

                    解法二:编程稍微复杂,时间复杂度略低,可以用于海量数据处理。

                    解法三:比解法二好一点,时间复杂度又降低了,并且不需要交换原来数组中的顺序,可以用于n很大k很小的场合,尤其是海量数据处理。

                    解法四:时间复杂度最低,线性时间即可解决,但是编程比较复杂,而且需要交换原来数组中数据的顺序,破坏了原来数组的顺序。

                    这四种解法,要选择适当的场合使用。解法三和解法四大家要牢牢掌握,可以用于其他类似的题目。

                    除了上述集中解法,还有其他的解法,比如利用哈希表,红黑树等其他数据结构,这里不再讲述。


0 0
原创粉丝点击