LeetCode 面试题40. 最小的k个数(Java)

面试题40. 最小的k个数

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

示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]

限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000

方法一:快速排序思想

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        //如果0个数或者数组为空,则返回空数组
        if(k==0||arr.length==0)
        {
            return new int[0];
        }
        //返回最大下标为k-1的数组
        return quickSearch(arr,0,arr.length-1,k-1);
    }
    public int[] quickSearch(int[] arr,int start,int end,int k)
    {
        //对数组进行切分,返回下标j
        int j=partition(arr,start,end);
        //比较下标j是否与我们所要求的个数相等
        if(j==k)
        {
            return Arrays.copyOf(arr,j+1);//如果相等,则返回j及j左边的所有元素
        }
        //否则,根据j和k的大小决定继续切分左段还是右段
        return j>k?quickSearch(arr,start,j-1,k):quickSearch(arr,j+1,end,k);
    }
    //返回下标j,使比arr[j]小的数在其左边,比arr[j]大的数在其右边
    public int partition(int[]arr,int start,int end)
    {
        int v=arr[start];
        int i=start;
        int j=end+1;
        while(true)
        {
            //从数组头向数组尾开始寻找,直到找到一个大于v的元素
            while(++i<=end&&arr[i]<v);
            //从数组尾向数组头开始寻找,直到找到一个小于v的元素
            while(--j>=start&&arr[j]>v);
            //如果i和j重逢,则跳出循环
            if(i>=j)
            {
                break;
            }
            //交换i和j位置上的数字
            int temp=arr[j];
            arr[j]=arr[i];
            arr[i]=temp;
        }
        //将v从数组头取出,放到它在数组中对应的位置,即左边所有数小于v,右边所有数大于v的位置
        arr[start]=arr[j];
        arr[j]=v;
        return j;//返回该位置
    }
}

方法二:大根堆

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k==0||arr.length==0)
        {
            return new int[0];
        }
        //将原本的小根堆改写比较器
        Queue<Integer> pq=new PriorityQueue<>((v1,v2)->v2-v1);
        for(int num:arr)//遍历数组中的元素
        {
            if(pq.size()<k)//如果大根堆没满,则将新元素放进去
            {
                pq.offer(num);
            }
            else if(num<pq.peek())//如果满了,比较新元素和堆顶元素的大小,如果小于堆顶元素
            {
                pq.poll();//将堆顶元素出队
                pq.offer(num);//将新元素放入
            }
        }
        //将堆里所有元素放入数组中输出
        int[] res=new int[k];
        int index=0;
        for(int resNum:pq)
        {
            res[index++]=resNum;
        }
        return res;
    }
}

知识点:

  1. PriorityQueue该队列的头部是相对于指定顺序的最小元素。
  2. 队列检索操作poll , remove ,peek和element访问在队列的头部的元件。
  3. public PriorityQueue(Comparator<? super E> comparator)
    创建具有默认初始容量的 PriorityQueue ,并根据指定的比较器对其元素进行排序

方法三:二叉搜索树BST

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k==0||arr.length==0)
        {
            return new int[0];
        }
        TreeMap<Integer,Integer> tm=new TreeMap<>();
        int count=0;
        for(int num:arr)//遍历数组元素
        {
            if(count<k)//如果计数值没达到所想要的个数,则继续
            {
                tm.put(num,tm.getOrDefault(num,0)+1);//将新元素放入map中
                count++;//计数加一
                continue;
            }
            Map.Entry<Integer,Integer> entry=tm.lastEntry();
            if(entry.getKey()>num)//如果map中最大键值大于新元素
            {
                tm.put(num,tm.getOrDefault(num,0)+1);//将新元素放入map中
                if(entry.getValue()==1)//如果最大键值的val等于1,即只有一个
                {
                    tm.pollLastEntry();//将该键值对poll出
                }
                else
                {
                    tm.put(entry.getKey(),entry.getValue()-1);//否则,将该键值的val减一,即个数减一
                }
            }    
        }
        int[] res=new int[k];
        int index=0;
        for(Map.Entry<Integer,Integer> entry:tm.entrySet())//遍历map中所有键值对
        {
            int countNum=entry.getValue();//先得到其val,即该key的个数
            while(countNum-->0)
            {
                res[index++]=entry.getKey();//在新数组中放入相应个数的该key
            }
        }
        return res;
    }
}

知识点:

  1. public Set<Map.Entry<K,V>> entrySet()
    返回此地图中包含的映射的Set视图。集合的迭代器按升序键顺序返回条目。
  2. public Map.Entry<K,V> lastEntry()
    返回与该地图中最大键相关的键值映射,如果地图为空,则返回 null 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值