• 如何在 10 亿数中找出前 1000 大的数


    点击蓝色“五分钟学算法”关注我哟

    加个“星标”,一起学算法

    640?wx_fmt=jpeg

    作者 | channingbreeze

    来源 | 互联网侦察


    小史是一个应届生,虽然学的是电子专业,但是自己业余时间看了很多互联网与编程方面的书,一心想进BAT互联网公司。


    640?wx_fmt=jpeg


    之前小史在 BAT 三家的面试中已经挂了两家,今天小史去了 BAT 中的最后一家面试了。


    简单的自我介绍后,面试官给了小史一个问题。


    640?wx_fmt=jpeg


    【面试现场】


    640?wx_fmt=jpeg


    题目:如何在 10 亿数中找出前 1000 大的数?


    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    小史:我可以用分治法,这有点类似快排中 partition 的操作。随机选一个数 t,然后对整个数组进行 partition ,会得到两部分,前一部分的数都大于 ,后一部分的数都小于 

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    小史:如果说前一部分总数大于 1000 个,那就继续在前一部分进行 partition 寻找。如果前一部分的数小于 1000 个,那就在后一部分再进行 partition ,寻找剩下的数。

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    小史:首先,partition 的过程,时间是 o(n)。我们在进行第一次 partition 的时候需要花费 ,第二次 partition 的时候,数据量减半了,所以只要花费 n/2,同理第三次的时候只要花费n/4,以此类推。而n + n/2 + n/4 + ...显然是小于 2n 的,所以这个方法的渐进时间只有 o(n)

    640?wx_fmt=jpeg

    (注:这里的时间复杂度计算只是简化计算版,真正严谨的数学证明可以参考算法导论相关分析。)

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg


    半分钟过去了。


    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg


    小史一时慌了神。


    640?wx_fmt=jpeg

    他回忆起了之前吕老师给他讲解 bitmap 时的一些细节。突然有了一个想法。


    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg


    小史在纸上画了画。


    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    640?wx_fmt=jpeg


    理解了算法之后,小史的代码写起来也是非常快,不一会儿就写好了:

    
     
    /** * @author xiaoshi on 2018/10/14. */public class TopN {    // 父节点    private int parent(int n) {        return (n - 1) / 2;    }    // 左孩子    private int left(int n) {        return 2 * n + 1;    }    // 右孩子    private int right(int n) {        return 2 * n + 2;    }    // 构建堆    private void buildHeap(int n, int[] data) {        for(int i = 1; i < n; i++) {            int t = i;            // 调整堆            while(t != 0 && data[parent(t)] > data[t]) {                int temp = data[t];                data[t] = data[parent(t)];                data[parent(t)] = temp;                t = parent(t);            }        }    }    // 调整data[i]    private void adjust(int i, int n, int[] data) {        if(data[i] <= data[0]) {            return;        }        // 置换堆顶        int temp = data[i];        data[i] = data[0];        data[0] = temp;        // 调整堆顶        int t = 0;        while( (left(t) < n && data[t] > data[left(t)])            || (right(t) < n && data[t] > data[right(t)]) ) {            if(right(t) < n && data[right(t)] < data[left(t)]) {                // 右孩子更小,置换右孩子                temp = data[t];                data[t] = data[right(t)];                data[right(t)] = temp;                t = right(t);            } else {                // 否则置换左孩子                temp = data[t];                data[t] = data[left(t)];                data[left(t)] = temp;                t = left(t);            }        }    }    // 寻找topN,该方法改变data,将topN排到最前面    public void findTopN(int n, int[] data) {        // 先构建n个数的小顶堆        buildHeap(n, data);        // n往后的数进行调整        for(int i = n; i < data.length; i++) {            adjust(i, n, data);        }    }    // 打印数组    public void print(int[] data) {        for(int i = 0; i < data.length; i++) {            System.out.print(data[i] + " ");        }        System.out.println();    }}
    public class TopN {

        // 父节点
        private int parent(int n) {
            return (n - 1) / 2;
        }

        // 左孩子
        private int left(int n) {
            return 2 * n + 1;
        }

        // 右孩子
        private int right(int n) {
            return 2 * n + 2;
        }

        // 构建堆
        private void buildHeap(int n, int[] data) {
            for(int i = 1; i < n; i++) {
                int t = i;
                // 调整堆
                while(t != 0 && data[parent(t)] > data[t]) {
                    int temp = data[t];
                    data[t] = data[parent(t)];
                    data[parent(t)] = temp;
                    t = parent(t);
                }
            }
        }

        // 调整data[i]
        private void adjust(int i, int n, int[] data) {
            if(data[i] <= data[0]) {
                return;
            }
            // 置换堆顶
            int temp = data[i];
            data[i] = data[0];
            data[0] = temp;
            // 调整堆顶
            int t = 0;
            while( (left(t) < n && data[t] > data[left(t)])
                || (right(t) < n && data[t] > data[right(t)]) ) {
                if(right(t) < n && data[right(t)] < data[left(t)]) {
                    // 右孩子更小,置换右孩子
                    temp = data[t];
                    data[t] = data[right(t)];
                    data[right(t)] = temp;
                    t = right(t);
                } else {
                    // 否则置换左孩子
                    temp = data[t];
                    data[t] = data[left(t)];
                    data[left(t)] = temp;
                    t = left(t);
                }
            }
        }

        // 寻找topN,该方法改变data,将topN排到最前面
        public void findTopN(int n, int[] data) {
            // 先构建n个数的小顶堆
            buildHeap(n, data);
            // n往后的数进行调整
            for(int i = n; i < data.length; i++) {
                adjust(i, n, data);
            }
        }

        // 打印数组
        public void print(int[] data) {
            for(int i = 0; i < data.length; i++) {
                System.out.print(data[i] + " ");
            }
            System.out.println();
        }

    }


    面试官看了一下。


    640?wx_fmt=jpeg


    小史熟练地介绍起了自己的项目,由于准备充分,小史聊起来游刃有余。面试官问的几个问题也进行了详细的解释。


    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    小史走后,面试官在系统中写下了面试评语:


    640?wx_fmt=jpeg


    【遇见吕老师】


    小史回到学校哼着歌走在校园的路上,正好碰到吕老师。


    640?wx_fmt=jpeg

    640?wx_fmt=jpeg


    小史把面试情况和吕老师说了一下。


    640?wx_fmt=jpeg

    640?wx_fmt=jpeg

    小史:感悟还挺深的。虽然平时做过 topN 的问题,知道分治法时间更少。但是碰到具体问题的时候还是要具体分析,这种大数据量的情况下反而用堆会更快。


    640?wx_fmt=jpeg

    640?wx_fmt=jpeg



    END

    640?wx_fmt=png

     原 创 热 文 推 荐 


    毕业十年后,我忍不住出了一份程序员的高考试卷

    一道腾讯面试题:厉害了我的杯

    十大经典排序算法动画与解析,看我就够了

    这或许是东半球分析十大排序算法最好的一篇文章

    面试官,我会写二分查找法!对,没有 bug 的那种!


    640?wx_fmt=png 你点的每个“在看”,我都认真当成了喜欢
  • 相关阅读:
    SQL 学习之路 (一)
    简单、易懂、通用的微信号、二维码轮播js
    本地phpstudy 新建站点运行步骤
    react-native 项目环境搭建
    JavaScript与DOM(下)
    JavaScript与DOM(上)
    ThisYes,this!
    编写高质量的JavaScript代码的基本要点
    变量对象(Variable Object)
    JavaScript核心
  • 原文地址:https://www.cnblogs.com/csnd/p/16675321.html
Copyright © 2020-2023  润新知