最小的k个数*****

题目描述

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

思路:

方法一:和上一个题目“数组中次数超过一半的数字”类似,平均时间复杂度是O(n),找出来的k个数字不一定是排序的,而且会修改输入的数组

注意:每次去一半的数组去寻找就好

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> res = new ArrayList<Integer>();
        int len = input.length;
        
        //边界的问题
        if(k>len || k<=0 || len ==0) return res;
        
        kuaipai(res , input , k , 0 , len-1);
        return res;
    }
    public void kuaipai(ArrayList<Integer> res , int [] input , int k, int i , int j){
        int pos = getPos(input , i , j );
        if(pos ==(k-1)){
            //把前k个放到res中,然后返回
            for(int t = i;t<k;t++){
                res.add(input[t]);
            }
            return ;
        }
        else if(pos <(k-1)){
            //把前pos个数放到res中,然后再去后半部分找
            for(int t = i;t<=pos;t++){
                res.add(input[t]);
            }
            kuaipai(res , input , k , pos+1,j);
        }else if(pos >(k-1)){
            //去前半部分找
            kuaipai(res , input , k , i , pos-1);
        }
    }
    
    public int getPos(int [] input , int low , int high){
        int i = low ;
        int j = high;
        int flag = input[low];
        while(i<j){
            while(i<j && input[i]<flag) i++;
            while(i<j && input[j]>flag) j--;
            swap(input , i , j);
        }
        if(input[i]>input[low]) i--;//就这里错了,这里一定要是> , 而不是>=
        swap(input , i , low);
        return i ;
    }
    public void swap(int [] input , int i , int j ){
        int t = input[i];
        input[i] = input[j];
        input[j] = t;
    }
}

方法二:O(nlogk)的算法,特别适合处理海量数据

 用最大堆保存这k个数,如果新的数字比堆顶小,新数入堆。

注意:java中的优先队列是基于堆实现的

抄一下别人的代码实现:

链接:https://www.nowcoder.com/questionTerminal/6a296eb82cf844ca8539b57c23e6e9bf
来源:牛客网

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
       ArrayList<Integer> result = new ArrayList<Integer>();
       int length = input.length;
       if(k > length || k == 0){
           return result;
       }
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
 
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        for (int i = 0; i < length; i++) {
            if (maxHeap.size() != k) {
                maxHeap.offer(input[i]);
            } else if (maxHeap.peek() > input[i]) {
                Integer temp = maxHeap.poll();
                temp = null;
                maxHeap.offer(input[i]);
            }
        }
        for (Integer integer : maxHeap) {
            result.add(integer);
        }
        return result;
    }
}

 

答主的方法时间复杂度只有O(nlogk), 因为堆中总共k个数,每插入或删除一个数,都要调整一次堆,即时间复杂度为O(logk), 共循环n次,插入的次数最坏情况为n次,删除为n-k次,而平均下来插入次数也不少,所以大致估计时间复杂度就在nlogk



因为compareTo方法代表的是指定数如果大于参数的话那么则返回1,实现了comparator中重写compare方法,如果返回值是正数的话,那么就让第二个参数在前面,如果是负数的话,就让第一个数在前面。在这个方法中,如果o2大于o1那么返回1,就让o2在前面,就成为了最大堆

 

https://www.cnblogs.com/yongh/p/9945539.html

这个博客写的很清楚:

【Java】 用PriorityQueue实现最大最小堆

PriorityQueue(优先队列),一个基于优先级堆的无界优先级队列。

实际上是一个堆(不指定Comparator时默认为最小堆),通过传入自定义的Comparator函数可以实现大顶堆。

PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(); //小顶堆,默认容量为11
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){ //大顶堆,容量11
    @Override
    public int compare(Integer i1,Integer i2){
        return i2-i1;
    }
});

 案例: 剑指offer(41) 最小的k个数

PriorityQueue的常用方法有:poll(),offer(Object),size(),peek()等。

  •   插入方法(offer()、poll()、remove() 、add() 方法)时间复杂度为O(log(n)) ;
  •   remove(Object) 和 contains(Object) 时间复杂度为O(n);
  •   检索方法(peek、element 和 size)时间复杂度为常量。

PriorityQueue(int initialCapacity, Comparator<? super E> comparator)
          使用指定的初始容量创建一个 PriorityQueue,并根据指定的比较器对元素进行排序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值