• 寻找最小的k个数


    问题描述:给定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忽略)。

     1 import java.util.Scanner;
     2 public class Qinkxiao {
     3     public static void heapadjust(int H[],int s,int m){   //调整堆
     4         int j,k;
     5         int rc=H[s];
     6         for( j=2*s;j<=m;j*=2)
     7         {
     8             if(j<m && H[j]<H[j+1])++j;
     9             if(rc>=H[j])break;
    10             H[s]=H[j];
    11             s=j;
    12         }
    13     
    14         H[s]=rc;
    15         
    16     }
    17     public static void createdui(int array[],int k){    //建初始堆
    18         for(int i=k/2;i>=1;--i)
    19             heapadjust(array,i,k);
    20     }
    21     public static void main(String[] args) {  
    22           int array[]={4,5,9,2,1,5,3,-5,20};
    23           int n=array.length-1;
    24           int k=3;
    25           System.out.print("n个整数为:");
    26           for(int i=1;i<=n;i++)
    27           {  
    28               System.out.print(array[i]+",");
    29            }
    30               System.out.println();
    31               createdui(array,k);                //建k个节点的初始堆
    32               for(int i=k+1;i<=n;i++)            //依次遍历后面的n-k个数
    33                   if(array[i]<array[1])
    34                       {
    35                       array[1]=array[i];
    36                       heapadjust(array,1,k);
    37                       }
    38               System.out.print("前"+k+"小个整数为:");    
    39               for(int i=1;i<=k;i++)
    40                   System.out.print(array[i]+",");
    41     }
    42 
    43 }
    View Code

    输出结果为:

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

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

                       具体的Java代码如下:

               

     1 import java.util.Scanner;
     2 public class Qinkxiao {
     3 
     4       public static int partition(int array[],int low,int high){         //快速排序中的划分方法
     5         int q=array[low];
     6         int m;
     7         while(low<high){
     8             while(low<high && array[high]>q)high--;
     9             m=array[low];array[low]=array[high];array[high]=m;
    10             while(low<high && array[low]<q)low++;
    11             m=array[low];array[low]=array[high];array[high]=m;
    12         }
    13         return low;
    14     }
    15 
    16     public static void main(String[] args) {  
    17           int array[]={4,5,9,2,1,5,3,-5,20};
    18           int n=array.length;
    19           int k=3;
    20           System.out.print("n个整数为:");
    21           for(int i=0;i<n;i++)
    22           {  
    23               System.out.print(array[i]+",");
    24            }
    25               System.out.println();
    26              
    27               int start=0,end=n-1;
    28               int index=partition(array,start,end);
    29               while(index!=k-1)                           //判断索引位置石佛符合要求
    30               {
    31                   if(index>k-1 )
    32                   { end=index-1;
    33                       index=partition(array,start,end);
    34                   }
    35                   else 
    36                   { start=index+1;
    37                       index=partition(array,start,end);
    38                   }
    39               }
    40               
    41               System.out.print("前"+k+"小个整数为:");    
    42               for(int i=0;i<k;i++)
    43                   System.out.print(array[i]+",");
    44     }
    45 
    46 }
    View Code

    输出结果为:

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

                  上述四种解法比较:

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

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

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

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

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

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

  • 相关阅读:
    Mysql 安装
    网站搭建 so easy
    git 命令!!!!!!!!!!!
    git branch 管理常用命令
    Java开发环境的搭建以及使用eclipse从头一步步创建java项目
    git 放弃本地修改 强制更新
    java算法之猴子排序睡眠排序
    sql业务需求,查询每个分类下的前两n条数据
    mysql安装
    linux服务自启
  • 原文地址:https://www.cnblogs.com/guozhenqiang/p/5430746.html
Copyright © 2020-2023  润新知