zoukankan      html  css  js  c++  java
  • Java实现寻找最小的k个数

    1 问题描述
    有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低。

    2 解决方案
    2.1 全部排序法

    先对这n个整数进行快速排序,在依次输出前k个数。

    package com.liuzhen.array_2;
    
    public class SearchMinK {
        //方法1:全部排序
        public void quickSort(int[] A,int start,int end){
            if(end > start){
                int k = LomutoPartition(A,start,end);
                quickSort(A,start,k-1);
                quickSort(A,k+1,end);
            }
        }
        //返回数值result,满足:     左边部分< A[result] <=右边部分
        public int LomutoPartition(int[] A,int start,int end){
            if(start >= end)
                return start;
            int begin = A[start];
            int result = start;
            for(int i = start + 1;i <= end;i++){
                if(A[i] < begin){
                    result++;
                    swap(A,i,result);
                }
            }
            swap(A,start,result);
            return result;
        }
        //交换数组m位置和n位置上的值
        public void swap(int[] arrayA,int m,int n){
            int temp = arrayA[m];
            arrayA[m] = arrayA[n];
            arrayA[n] = temp;
        }
        //输出数组前k个元素
        public void printArrayK(int[] array,int k){
            for(int i = 0;i < k;i++){
                System.out.print(array[i]+" ");
            }
        }
    public static void main(String[] args){
            SearchMinK test = new SearchMinK();
            int[] A = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
            test.quickSort(A, 0, A.length-1);
            System.out.println("对数组进行排序后结果:");
            for(int i = 0;i < A.length;i++)
                System.out.print(A[i]+" ");
            System.out.println("
    "+"输出数组最小的5个数:");
            test.printArrayK(A, 5);
    
        }
    }
    

    运行结果:

    对数组进行排序后结果:
    1 1 2 2 3 3 3 4 4 4 5 5 6 6 7 8 9 12 32 34 
    输出数组最小的5个数:
    1 1 2 2 3 
    

    2.2 部分排序法
    具体操作步骤如下:

    (1)遍历n个数,把最先遍历到的k个数存入到大小为k的数组中,假设他们就是最小的k个数;

    (2)利用选择排序或交换排序找到这k个元素中的最大值kmax;

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

    具体代码如下:

    package com.liuzhen.array_2;
    
    public class SearchMinK {
    //方法2:部分排序
        public void getArrayMinK(int[] A,int k){
            if(k > A.length)
                return;
            while(true){
                int max = getMaxArrayK(A,k);  //当前数组前k个元素中的最大值
                int count = 0;
                for(int i = k;i < A.length;i++){
                    if(A[max] > A[i])
                        swap(A,max,i);
                    else
                        count++;
                }
                if(count == A.length-k)
                    break;
            }
            System.out.println("
    "+"使用方法2进行部分排序后的结果:");
            for(int i = 0;i < A.length;i++)
                System.out.print(A[i]+" ");
            System.out.println("
    部分排序选出数组中最小的"+k+"个数:");
            for(int i = 0;i < k;i++)
                System.out.print(A[i]+" ");
        }
        
        //获取数组前k个元素的最大值的数组下标
        public int getMaxArrayK(int[] A,int k){
            int result = 0;
            if(k > A.length)
                return 0;
            for(int i = 0;i < k;i++){
                if(A[i] > A[result])
                    result = i;
            }
            return result;
        }
    //交换数组m位置和n位置上的值
        public void swap(int[] arrayA,int m,int n){
            int temp = arrayA[m];
            arrayA[m] = arrayA[n];
            arrayA[n] = temp;
        }
    public static void main(String[] args){
            SearchMinK test = new SearchMinK();
    
            int[] B = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
            test.getArrayMinK(B, 5);
    
        }
    

    运行结果:

    使用方法2进行部分排序后的结果:
    1 1 2 2 3 9 8 7 6 5 4 5 12 32 4 3 3 4 6 34 
    部分排序选出数组中最小的5个数:
    1 1 2 2 3 
    

    2.3 用堆代替数组法
    此处思想和2.2中一致,唯一区别就是在寻找kmax时,是使用堆排序的思想。

    具体代码如下:

    package com.liuzhen.array_2;
    
    public class SearchMinK {
    //方法3:用堆来代替数组
        /*
         * 函数功能:对数组A前k个元素进行堆排序
         */
        public void heapBottomUp(int[] A,int k){
            for(int i = (k-1)/2;i >= 0;i--){
                int temp = i;
                int tempV = A[temp];
                boolean heap = false;
                while(!heap && 2*temp < k-1){
                    int j = 2*temp + 1;
                    if(j < k-1){
                        if(A[j] < A[j+1])
                            j = j + 1;
                    }
                    if(tempV >= A[j])
                        heap = true;
                    else{
                        A[temp] = A[j];
                        temp = j;
                    }
                }
                A[temp] = tempV;
            }
        }
        
        public void getArrayMinK2(int[] A,int k){
            heapBottomUp(A,k);
            while(true){
                int count = 0;
                for(int i = k;i < A.length;i++){
                   if(A[i] < A[0]){
                       swap(A,i,0);
                      heapBottomUp(A,k);
                   }
                   else
                       count++;
                }
                if(count == A.length-k)
                    break;
            }
            System.out.println("
    "+"使用方法3进行部分堆排序后的结果:");
            for(int i = 0;i < A.length;i++)
                System.out.print(A[i]+" ");
            System.out.println("
    部分排序选出数组中最小的"+k+"个数:");
            for(int i = 0;i < k;i++)
                System.out.print(A[i]+" ");
        }
    //交换数组m位置和n位置上的值
        public void swap(int[] arrayA,int m,int n){
            int temp = arrayA[m];
            arrayA[m] = arrayA[n];
            arrayA[n] = temp;
        }
    public static void main(String[] args){
            SearchMinK test = new SearchMinK();
    
            int[] D = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
            test.getArrayMinK2(D, 5);
    
        }
    }
    

    运行结果:

    使用方法3进行部分堆排序后的结果:
    3 2 2 1 1 9 8 7 6 5 4 5 12 32 4 3 3 4 6 34 
    部分排序选出数组中最小的5个数:
    3 2 2 1 1 
    

    2.4线性选择算法
    看具体代码即可理解其中蕴含的思想。

    package com.liuzhen.array_2;
    
    public class SearchMinK {
    //返回数值result,满足:     左边部分< A[result] <=右边部分
        public int LomutoPartition(int[] A,int start,int end){
            if(start >= end)
                return start;
            int begin = A[start];
            int result = start;
            for(int i = start + 1;i <= end;i++){
                if(A[i] < begin){
                    result++;
                    swap(A,i,result);
                }
            }
            swap(A,start,result);
            return result;
        }
    
        //方法4:线性选择法
        public void getArrayMinK3(int[] A,int k){
            int start = 0;
            int end = A.length - 1;
            int tempK = LomutoPartition(A,start,end);
            while(tempK != k){
                if(tempK > k){
                    end = tempK - 1;
                    tempK = LomutoPartition(A,start,end);
                }
                if(tempK < k){
                    start = tempK + 1;
                    tempK = LomutoPartition(A,start,end);
                }
            }
            System.out.println("
    "+"使用方法4进行快速选择排序后的结果:");
            for(int i = 0;i < A.length;i++)
                System.out.print(A[i]+" ");
            System.out.println("
    部分排序选出数组中最小的"+k+"个数:");
            for(int i = 0;i < k;i++)
                System.out.print(A[i]+" ");
        }
    public static void main(String[] args){
            SearchMinK test = new SearchMinK();
    
            int[] E = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
            test.getArrayMinK3(E, 5);
        }
    }
    

    运行结果:

    使用方法4进行快速选择排序后的结果:
    1 2 2 1 3 3 3 4 5 5 4 4 6 8 6 7 9 32 12 34 
    部分排序选出数组中最小的5个数:
    1 2 2 1 3 
    

    此处附上四种方法的完整代码

    package com.liuzhen.array_2;
    
    public class SearchMinK {
        //方法1:全部排序
        public void quickSort(int[] A,int start,int end){
            if(end > start){
                int k = LomutoPartition(A,start,end);
                quickSort(A,start,k-1);
                quickSort(A,k+1,end);
            }
        }
        //返回数值result,满足:     左边部分< A[result] <=右边部分
        public int LomutoPartition(int[] A,int start,int end){
            if(start >= end)
                return start;
            int begin = A[start];
            int result = start;
            for(int i = start + 1;i <= end;i++){
                if(A[i] < begin){
                    result++;
                    swap(A,i,result);
                }
            }
            swap(A,start,result);
            return result;
        }
        //交换数组m位置和n位置上的值
        public void swap(int[] arrayA,int m,int n){
            int temp = arrayA[m];
            arrayA[m] = arrayA[n];
            arrayA[n] = temp;
        }
        //输出数组前k个元素
        public void printArrayK(int[] array,int k){
            for(int i = 0;i < k;i++){
                System.out.print(array[i]+" ");
            }
        }
        
        //方法2:部分排序
        public void getArrayMinK(int[] A,int k){
            if(k > A.length)
                return;
            while(true){
                int max = getMaxArrayK(A,k);  //当前数组前k个元素中的最大值
                int count = 0;
                for(int i = k;i < A.length;i++){
                    if(A[max] > A[i])
                        swap(A,max,i);
                    else
                        count++;
                }
                if(count == A.length-k)
                    break;
            }
            System.out.println("
    "+"使用方法2进行部分排序后的结果:");
            for(int i = 0;i < A.length;i++)
                System.out.print(A[i]+" ");
            System.out.println("
    部分排序选出数组中最小的"+k+"个数:");
            for(int i = 0;i < k;i++)
                System.out.print(A[i]+" ");
        }
        
        //获取数组前k个元素的最大值的数组下标
        public int getMaxArrayK(int[] A,int k){
            int result = 0;
            if(k > A.length)
                return 0;
            for(int i = 0;i < k;i++){
                if(A[i] > A[result])
                    result = i;
            }
            return result;
        }
        
        //方法3:用堆来代替数组
        /*
         * 函数功能:对数组A前k个元素进行堆排序
         */
        public void heapBottomUp(int[] A,int k){
            for(int i = (k-1)/2;i >= 0;i--){
                int temp = i;
                int tempV = A[temp];
                boolean heap = false;
                while(!heap && 2*temp < k-1){
                    int j = 2*temp + 1;
                    if(j < k-1){
                        if(A[j] < A[j+1])
                            j = j + 1;
                    }
                    if(tempV >= A[j])
                        heap = true;
                    else{
                        A[temp] = A[j];
                        temp = j;
                    }
                }
                A[temp] = tempV;
            }
        }
        
        public void getArrayMinK2(int[] A,int k){
            heapBottomUp(A,k);
            while(true){
                int count = 0;
                for(int i = k;i < A.length;i++){
                   if(A[i] < A[0]){
                       swap(A,i,0);
                      heapBottomUp(A,k);
                   }
                   else
                       count++;
                }
                if(count == A.length-k)
                    break;
            }
            System.out.println("
    "+"使用方法3进行部分堆排序后的结果:");
            for(int i = 0;i < A.length;i++)
                System.out.print(A[i]+" ");
            System.out.println("
    部分排序选出数组中最小的"+k+"个数:");
            for(int i = 0;i < k;i++)
                System.out.print(A[i]+" ");
        }
        
        //方法4:线性选择法
        public void getArrayMinK3(int[] A,int k){
            int start = 0;
            int end = A.length - 1;
            int tempK = LomutoPartition(A,start,end);
            while(tempK != k){
                if(tempK > k){
                    end = tempK - 1;
                    tempK = LomutoPartition(A,start,end);
                }
                if(tempK < k){
                    start = tempK + 1;
                    tempK = LomutoPartition(A,start,end);
                }
            }
            System.out.println("
    "+"使用方法4进行快速选择排序后的结果:");
            for(int i = 0;i < A.length;i++)
                System.out.print(A[i]+" ");
            System.out.println("
    部分排序选出数组中最小的"+k+"个数:");
            for(int i = 0;i < k;i++)
                System.out.print(A[i]+" ");
        }
        public static void main(String[] args){
            SearchMinK test = new SearchMinK();
            int[] A = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
            test.quickSort(A, 0, A.length-1);
            System.out.println("对数组进行排序后结果:");
            for(int i = 0;i < A.length;i++)
                System.out.print(A[i]+" ");
            System.out.println("
    "+"输出数组最小的5个数:");
            test.printArrayK(A, 5);
            int[] B = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
            test.getArrayMinK(B, 5);
            int[] C = {2,9,7,6,5,8};
            test.heapBottomUp(C, 6);
            System.out.println("
    C数组:");
            for(int i = 0;i < C.length;i++)
                System.out.print(C[i]+" ");
            int[] D = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
            test.getArrayMinK2(D, 5);
            int[] E = {9,8,7,5,4,3,2,1,6,3,4,5,12,32,3,2,1,4,6,34};
            test.getArrayMinK3(E, 5);
        }
    }
    
    完整代码
    
  • 相关阅读:
    将"089,0760,009"变为 89,760,9
    单向循环链表
    双链表复习
    【C语言】scanf()输入浮点型数据
    【C语言】一元二次方程(求实根和虚根)
    输入一个三位正整数,输出百位数,十位数,个位数
    输入身份证号,输出出生日期
    比较四个数的大小
    比较三个数的大小
    比较两个数的大小
  • 原文地址:https://www.cnblogs.com/a1439775520/p/13077957.html
Copyright © 2011-2022 走看看