zoukankan      html  css  js  c++  java
  • LeetCode-Interview40-最小的k个数

    题目描述

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

    我的题解

    暴力排序

    把数组升序排好,取出最前的k个即可,很暴力,比较低效, 仅适合与k接近与数组长度。

        //java 8ms
        public int[] getLeastNumbers(int[] arr, int k) {
            Arrays.sort(arr);
            int [] res = new int[k];
            for (int i=0;i<k;i++){
                res[i]=arr[i];
            }
            return res;
        }
    

    有限排序

    想法是只找出k个最小数,不要对其他的数进行排序

        //效率还是不行,比上面全排序还差,这个排序算法需要改进!
        public int[] getLeastNumbers(int[] arr, int k) {
            int len = arr.length;
            int [] res = new int[k];
            for (int i=0;i<k;i++){
                int min = arr[i];
                int minIndex=i;
                for (int j=i+1;j<len;j++){
                    if (arr[j]<min){
                        min = arr[j];
                        minIndex=j;
                    }
                }
                if (minIndex!=i) arr[minIndex]=arr[i];
                res[i]=min;
            }
    
            return res;
        }
    

    利用快速排序

    快排:

        private int [] quickSort(int []arr,int left,int right){//right=数组长-1
            int L=left,R=right;
            if (L<R){//至少2两个元素
                int tmp = arr[L];//取基准
                while (L<R){
                    //找出右边比基准小的第一个数,
                    while (L<R && arr[R]>=tmp)R--;
                    //找到了之后拿到左边:如果没有匹配的则L==R,相当于没替换
                    arr[L]=arr[R];
                    //找出左边比基准大的第一个元素
                    while (L<R && arr[L]<=tmp)L++;
                    //找到之后拿到右边
                    arr[R]=arr[L];
                }
                //跳出循环是L==R,arr[L]是中间的那个数,大小数居两边;
                //注意上面:去掉内部while,发现执行了L=R’,R‘=L‘,即这个基准被改变了,需要还原:
                arr[L] = tmp;
                //那么左右递归,对子数组排序
                quickSort(arr,left,L-1);
                quickSort(arr,R+1,right);
            }
    
            return  arr;
        }
    

    快排是选一个分解值,然后把小于的移到一边,大于的移到另一边,会返回分界值所在的下标。
    这里每次找一个分界值,得出其下标m,m和K比较:
    k=m:说明刚好找到
    k<m:说明top k在前m个,递归(0,m-1)
    k>m:说明还有m-k个数在(m+1,R)中,递归

        public int[] getLeastNumbers(int[] arr, int k) {
            if (k == 0) return new int[0];
            else if(arr.length<=k)return arr;
            topK(arr,0,arr.length-1,k);
            int res [] = new int[k];
            for (int i=0;i<k;i++)res[i]=arr[i];
            return res;
        }
        private void topK(int []a,int left,int right,int k){
            int m=partition(a,left,right);
            if (m==k)return ;
            else if (m>k)topK(a,left,m-1,k);
            else topK(a,m+1,right,k);
        }
        //分区函数:
        private int partition(int []a,int L,int R){
            int tmp = a[L];
            while (L!=R){
                while (L<R && a[R]>=tmp)R--;
                a[L]=a[R];
                while(L<R && a[L]<=tmp)L++;
                a[R]=a[L];
            }
            a[R]=tmp;
            return R;
        }
    

    这里分区函数是将快排的递归为1的时候,这个赋值比较多,性能其实不太好
    看到这个:

        int partition_better(int[] a, int lo, int hi) {
            int i = lo;
            int j = hi + 1;
            int v = a[lo];
            while (true) {
                while (a[++i] < v) //左到右扫描,找到第一个 >= v的
                    if (i == hi) break;//没有,退出
                while (a[--j] > v)//右到左扫描,找到第一个 <= v的
                    if (j == lo)break;//没有,退出
                if (i >= j)  break;//扫描完毕,退出
                swap(a, i, j);//找到后交换值
            }
            swap(a, lo, j);//j是分界下标,把分界值拿过去
            return j;
        }
        void swap(int[] a, int i, int j) {
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    

    其他题解

    最大堆 O(NlogK)

    // 保持堆的大小为K,然后遍历数组中的数字,遍历的时候做如下判断:
    // 1. 若目前堆的大小小于K,将当前数字放入堆中。
    // 2. 否则判断当前数字与大根堆堆顶元素的大小关系,如果当前数字比大根堆堆顶还大,这个数就直接跳过;
    //    反之如果当前数字比大根堆堆顶小,先poll掉堆顶,再将该数字放入堆中。
    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[pq.size()];
            int idx = 0;
            for(int num: pq) {
                res[idx++] = num;
            }
            return res;
        }
    }
    

    二叉搜索树

    class Solution {
        public int[] getLeastNumbers(int[] arr, int k) {
            if (k == 0 || arr.length == 0) {
                return new int[0];
            }
            // TreeMap的key是数字, value是该数字的个数。
            // cnt表示当前map总共存了多少个数字。
            TreeMap<Integer, Integer> map = new TreeMap<>();
            int cnt = 0;
            for (int num: arr) {
                // 1. 遍历数组,若当前map中的数字个数小于k,则map中当前数字对应个数+1
                if (cnt < k) {
                    map.put(num, map.getOrDefault(num, 0) + 1);
                    cnt++;
                    continue;
                } 
                // 2. 否则,取出map中最大的Key(即最大的数字), 判断当前数字与map中最大数字的大小关系:
                //    若当前数字比map中最大的数字还大,就直接忽略;
                //    若当前数字比map中最大的数字小,则将当前数字加入map中,并将map中的最大数字的个数-1。
                Map.Entry<Integer, Integer> entry = map.lastEntry();
                if (entry.getKey() > num) {
                    map.put(num, map.getOrDefault(num, 0) + 1);
                    if (entry.getValue() == 1) {
                        map.pollLastEntry();
                    } else {
                        map.put(entry.getKey(), entry.getValue() - 1);
                    }
                }
                
            }
    
            // 最后返回map中的元素
            int[] res = new int[k];
            int idx = 0;
            for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
                int freq = entry.getValue();
                while (freq-- > 0) {
                    res[idx++] = entry.getKey();
                }
            }
            return res;
        }
    }
    

    计数排序

    class Solution {
        public int[] getLeastNumbers(int[] arr, int k) {
            if (k == 0 || arr.length == 0) {
                return new int[0];
            }
            // 统计每个数字出现的次数
            int[] counter = new int[10001];
            for (int num: arr) {
                counter[num]++;
            }
            // 根据counter数组从头找出k个数作为返回结果
            int[] res = new int[k];
            int idx = 0;
            for (int num = 0; num < counter.length; num++) {
                while (counter[num]-- > 0 && idx < k) {
                    res[idx++] = num;
                }
                if (idx == k) {
                    break;
                }
            }
            return res;
        }
    }
    

    哈希

    int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize){
        int hash[10001] = {0};
        int *ret;
        int i,j;
    
        if(k >= arrSize) {
            *returnSize = arrSize;
            return arr;
        }
    
        ret = malloc(sizeof(int) * k);
    
        for(i = 0; i < arrSize; i++) {
            hash[arr[i]]++;
        }
        
        i = 0; j = 0;
        while(i < k) {
            if(hash[j]) {
                ret[i] = j;
                hash[j]--;
                i++;
            } else {
                j++;
            } 
        }
        *returnSize = k;
        return ret;
    }
    

    桶计数

    class Solution {
    public:
        vector<int> getLeastNumbers(vector<int>& arr, int k) {
            vector<int> ret;
            int m[10002];
            memset(m, 0, sizeof(m));
            for (auto i : arr) {
                m[i] ++;
            }
            for (int i = 0; i < 10002; i ++) {
                if (k <= 0) {
                    break;
                }
                int count = k < m[i] ? k : m[i];
                while (count --) {
                    ret.push_back(i);
                }
                k -= m[i];
            }
            return ret;
        }
    };
    
  • 相关阅读:
    gitlab+gerrit+jenkins持续集成框架
    多线程自动翻页爬虫
    爬虫超级简单入门
    完整开发流程管理提升与系统需求分析过程 随堂笔记(day 1) 【2019/10/14】
    Logistic Regression
    cmdb项目-2
    cmdb项目-3
    cmdb全总结
    crm-1
    crm-2
  • 原文地址:https://www.cnblogs.com/XT-xutao/p/12535659.html
Copyright © 2011-2022 走看看