剑指offer:最小的K个数

Posted 一只菜鸡的奋斗史

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指offer:最小的K个数相关的知识,希望对你有一定的参考价值。

题意描述

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

解题思路

一、冒泡排序/选择排序

对数组进行排序,从数组中取出最小的k个数。不需要对数组全部排序,只需要找出前k个数即可。

二、快速排序

利用快速排序划分的思想,选择一个基数index,将基数左右划分为两部分。

  1. 位于index左边的数字都小于index对应的值,右边都大于index指向的值
  2. 所以,当index > k-1时,表示k个最小数字一定在index的左边,此时,只需要对index的左边进行划分即可
  3. index < k-1时,说明index及index左边数字还没能满足k个数字,需要继续对k右边进行划分
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        	ArrayList<Integer> list = new ArrayList<Integer>(k);
            if(input == null)	//输入校验
                return null;	
            if(k > input.length)
                return list;
            int low = 0;
            int high = input.length - 1;
            int index = partition(input,low,high);	//划分数组,并找到基数所在的位置
            while(index != k-1){	
                if(index > k-1){
                    high = index - 1;	//向左划分
                }else{
                    low = index + 1;	//向右划分
                }
                index = partition(input,low,high);	//继续划分数组,寻找基数所在位置
            }
           for(int i = 0; i < k; i++){
               list.add(input[i]);
           }
            return list;
        }
        //划分操作
        public int partition(int[] array,int start,int end){
            int pivot = array[start];	//数组首位作为基数
            //循环结束,将数组划分为两部分
            while(start < end){
                //从右向左查找,array[end] 《 pivot 的位置
                while(start < end && array[end] >= pivot) end--;
                //交换,放到左边
                array[start] = array[end];
                //从左向右查找,array[start] 》 pivot 的位置
                while(start < end && array[start] <= pivot) start++;
                //交换,放到右边
                array[end] = array[start];
            }
            //start最后指向的位置 就是 基数所在位置
            array[start] = pivot; 
            return start;	
        }

三、堆排序思想

  • 可以先创建一个大小为k的数据容器来存储最小的k个数字,从输入的n个整数中一个一个读入放入该容器中,如果容器中的数字少于k个,则返回null

  • 如果容器中已有k个数字,而数组中还有值未加入,此时就不能直接插入了,而需要替换容器中的值。按以下步骤进行插入:

    1. 先找到容器中的最大值
    2. 将待查入值和最大值比较,如果待查入值大于容器中的最大值,则直接舍弃这个待查入值即可;如果待查入值小于容器中的最小值,则用这个待查入值替换掉容器中的最大值
    3. 重复上述步骤,容器中最后就是整个数组的最小k个数字

对于这个容器的实现,我们可以使用最大堆的数据结构,最大堆中,根节点的值大于它的子树中的任意节点值。Java中的TreeSet类实现了红黑树的功能,它底层是通过TreeMap实现的,TreeSet中的数据会按照插入数据自动升序排列(按自然顺序)。因此我们直接将数据依次放入到TreeSet中,数组就会自动排序。

    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
             ArrayList<Integer> list = new ArrayList<>();
            if(input == null || k<=0 || k>input.length) return list;	//输入校验
            TreeSet<Integer> set = new TreeSet<>();
             for(int i=0;i<input.length;i++){
                 set.add(input[i]);	//数组元素添加入set
             }
            int i=0;
        	//取出前k个值
            for(Integer item : set){	
                if(i >= k) break;
                list.add(item);
                i++;
            }
            return list;
        }

以上是关于剑指offer:最小的K个数的主要内容,如果未能解决你的问题,请参考以下文章

剑指offer--40最小的k个数

剑指offer---最小的K个数

剑指 Offer 40. 最小的k个数

最小的K个数-剑指Offer

剑指offer:最小k个数

剑指 Offer 40. 最小的k个数