题解 | #最小的K个数#

最小的K个数

http://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf

jz29:给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组
Java的Arrays类中的sort()方法对数组进行排序:
Arrays.sort(int[] a)
对一个数组的所有元素按从小到大进行排序
Arrays.sort(int[] a, int fromIndex, int toIndex)

对数组部分元素排序,也就是对数组a的下标从fromIndex到toIndex-1的元素排序
版本1:

import java.util.ArrayList;
import java.util.Arrays;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
         ArrayList<Integer> output=new ArrayList<>();
        if(k>input.length){
            //k>数组input的长度时返回一个空数组
            return output;
        }
        //Arrays.sort(int[] a)对一个数组的所有元素按从小到大进行排序
        Arrays.sort(input);
        for(int i=0;i<k;i++){
            output.add(new Integer(input[i]));
        }
        return output;
    }
}

堆排序:
堆排序,复杂度是o(nlogn),比较稳定适合大数据量的排序,如果是快排的话大数据量容易引起OOM

堆通常是一个可以被看作一棵完全二叉树的数组
堆分为大顶堆和小顶堆
图片说明
大顶堆:对于每一个节点,它都大于它的左孩子和右孩子
小顶堆:对于每一个节点,它都小于它的左孩子和右孩子
假如堆顶元素root的下标是从0开始的,那么对于某一个节点(下标为index),左孩子L(下标为2index+1),右孩子R(下标为2index+2)

选用大顶堆:
使用k个节点维护大顶堆
题中要求最小的四个数字(即维护k个节点的大顶堆)
就选取数组中四个数字(4,5,1,6)去组成四个节点的大顶堆
然后遍历剩下的N-k个数据,选出小于root的元素替换root,并维护堆为大顶堆

维护一个堆让它成为一个大顶堆方法:
自底向上从非叶子节点下标开始维护(非叶子节点下标=节点个数/2-1)
示例:
(3为非叶子结点 4,5,6,7为叶子结点)
图片说明
第一次维护:非叶子结点6和它的左孩子8比较,8大,互换位置(将大的数往上层换)
图片说明
第二次维护:节点1和它的左孩子7(选取孩子节点中最大的)比较,7大,互换位置
图片说明
第三次维护:节点5和它的左孩子8比较,8大,互换位置;此时节点5的左孩子6又比节点5大,再互换位置
图片说明
第四次维护:节点4和它的左孩子8比较,8大,互换位置;此时节点4的左孩子6又比节点4大,互换位置;接着节点4的左孩子5又比节点4大,再互换位置
图片说明
图片说明
遍历剩下的len-k个数据
如果当前位置数据小于堆顶元素root,用该数据替换root,然后维护堆让它成为一个大顶堆
将大顶堆中节点元素进行升序操作:
将堆顶元素与最后一个元素交换位置(这最后一个元素以后不会参加到堆的维护当中)
图片说明

 import java.util.ArrayList;
import java.util.Arrays;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        if (k > input.length || k == 0) {
            //返回空对象
            return new ArrayList<>();
        }

        int[] a = new int[k];//1.用数组模拟k个节点的大顶堆(堆通常是一个可以被看作一棵完全二叉树的数组)
//        for(int i=0;i<k;i++){
//            a[i]=input[i];
//        }
        //等同于
        //初始化堆中元素
        System.arraycopy(input, 0, a, 0, k);//从input数组中下标为0的数据开始复制k个数据到a数组从下标为0开始的位置,即intput[0]..input[k-1]复制到a[0]..a[k-1]
        //2.维护堆让它成为一个大顶堆
        //自底向上从非叶子节点下标开始维护(非叶子节点下标=节点个数/2-1)
        for (int i = k / 2 - 1; i >= 0; i--) {
            //维护大顶堆:非叶子结点和它的最大的孩子节点比较,孩子节点大,就互换位置(将大的数往上层换)
            initiate(i, a, k);
        }
        //3.遍历剩下的len-k个数据
        //如果当前位置数据小于堆顶元素root,用该数据替换root,然后维护堆让它成为一个大顶堆
        for (int i = k; i < input.length; i++) {
            //当前元素小于root
            if (input[i] < a[0]) {//a[0]:堆顶元素root
                a[0] = input[i];
                initiate(0, a, k);
            }
        }
        //4.将大顶堆中节点元素进行升序操作
        for(int i=a.length-1;i>0;i--){
            //将堆顶元素与最后一个元素交换位置(这最后一个元素以后不会参加到堆的维护当中)
            int temp=a[i];
            a[i]=a[0];
            a[0]=temp;
            initiate(0,a,i);
        }
        ArrayList<Integer> ans = new ArrayList<>();
        for(int x:a){
            ans.add(x);
        }
        return ans;
    }

    private void initiate(int index, int[] a, int k) {
        //假如堆顶元素root的下标是从0开始的,那么对于某一个节点(下标为index),左孩子L(下标为2*index+1),右孩子R(下标为2*index+2)
        //index维护当前堆的下标

        int temp=a[index];//保存当前位置的值
        for(int in=2*index+1;in<k;in=2*in+1){
            if(in+1<k && a[in+1]>a[in]){
                //a[in]左孩子 a[in+1]右孩子
                //取出当前位置的左右孩子中最大的一个
                in++;
            }
            //比较当前位置值与其最大孩子节点值,孩子节点大就互换位置
            if(a[in]>temp){//a[7]=8>a[3]=6
                a[index]=a[in];//换值//a[3]=8
                index=in;//换下标,
            }else{
                break;
            }
        }
        a[index]=temp;//a[7]=6
    }
}

图片说明

全部评论

相关推荐

2025-12-01 20:05
宁波职业技术学院 C++
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务