NC119 最小的K个数.牛客网

目录

描述

示例1

示例2

示例3

思想:快速排序、堆排序-优先级队列、手写大顶堆

代码


描述

给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。

数据范围:0≤k,n≤100000≤k,n≤10000,数组中每个数的大小0≤val≤10000≤val≤1000

要求:空间复杂度 O(n)O(n) ,时间复杂度 O(nlogn)O(nlogn)

示例1

输入:

[4,5,1,6,2,7,3,8],4 

复制返回值:

[1,2,3,4]

复制说明:

返回最小的4个数即可,返回[1,3,2,4]也可以        

示例2

输入:

[1],0

复制返回值:

[]

示例3

输入:

[0,1,2,1,2],3

复制返回值:

[0,1,1]

复制

思想:快速排序、堆排序-优先级队列、手写大顶堆

代码

import java.util.ArrayList;

public class Solution {
     //快速排序
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> arr = new ArrayList<>();
        int len = input.length;
//         if(k == 0 || k == len) return input.toList(); 
        if(k < len){
            quickSort(input, k, 0, len-1);
        }
        for(int i = 0; i < k; i++){
            arr.add(input[i]);
        }
        return arr;
    }
    public void quickSort(int[] input, int k, int a, int b){
        if(a >= b) return;
        int i = a;
        int j = b;
        int temp = input[a];
        while(i < j){
            while(i < j && input[j] >= temp) j--;
            while(i < j && input[i] <= temp) i++;
            swap(input, i, j);
        }
        swap(input, i, a);
        if(i == k-1){
            return;
        }
        quickSort(input, k, a, i-1);
        quickSort(input, k, i+1, b);
    }
    public void swap(int[] input, int a, int b){
        int temp = input[a];
        input[a] = input[b];
        input[b] = temp;
    }




    //大顶堆 - 优先级队列
    public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
        ArrayList<Integer> res = new ArrayList<>(k);
        //根据题意要求,如果K>数组的长度,返回一个空的数组
        if (k > input.length || k == 0)
            return res;
        //创建最大堆
        PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>(){
            public int compare(Integer i1, Integer i2) {
                //降序
                return i2.compareTo(i1);
            }
        });
        //先在堆中放数组的前k个元素
        for (int i = 0; i < k; ++i) {
            queue.offer(input[i]);
        }
        //因为是最大堆,也就是堆顶的元素是堆中最大的,遍历数组后面元素的时候,
        //如果当前元素比堆顶元素大,就把堆顶元素给移除,然后再把当前元素放到堆中,
        for (int i = k; i < input.length; ++i) {
            if (queue.peek() > input[i]) {
                queue.poll();
                queue.offer(input[i]);
            }
        }
        //最后再把堆中元素转化为数组
        for (int i = 0; i < k; ++i) {
            res.add(queue.poll());
        }
        return res;
    }




    //手写大顶堆
import java.util.ArrayList;
 
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        // [4,5,1,6,2,7,3,8],0
        if (input == null || k > input.length || k <= 0)
            return list;
        int[] target = new int[k];
        int len = input.length;
        for (int i = 0; i < len; ++i) {
            if (i < k) {
                target[i] = input[i];
                heapInsertSiftUp(target, i, target[i]);
            } else {
                if (target[0] > input[i]) { // 最大堆下沉
                    target[0] = input[i];
                    siftDown(target, 0, target[0]);
                    // 相比优先级队列,这里不会offer操作(里面有上浮),少了一步上浮调整,效率高了不止一丁点
                }
            }
        }
        for (int i = 0; i < k; ++i) {
            list.add(target[i]);
        }
        return list;
    }
 
    private void heapInsertSiftUp(int[] target, int index, int x) {
        while (index > 0) {
            int parent = (index - 1) >>> 1;
            if (greater(x, target[parent])) {
                target[index] = target[parent]; // 往下拉,避免直接上浮覆盖前面的值
                index = parent;
            } else {
                break;
            }
        }
        target[index] = x;
    }
 
    private boolean greater(int i, int j) {
        return i > j;
    }
 
    private void siftDown(int[] target, int k, int x) {
        int half = target.length >>> 1;
        while (k < half) {
            int child = (k << 1) + 1; // 默认先左孩子
            int big = target[child];
            int right = child + 1;
            if (right < target.length && greater(target[right], big)) {
                big = target[right];
                child = right; // 可以直接一步big = target[child = right];
            }
            if (greater(x, big)) // x比子节点中的最大值还大,已经是大顶堆了
                break; // 往上拉不动了,准备退出把最初堆顶的结点赋值到上一个结点
            target[k] = big; // 往上拉
            k = child;
        }
        target[k] = x;
    }
}



   
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值