精华内容
下载资源
问答
  • 大数据查找中位数

    千次阅读 2020-12-28 21:24:25
    所有数据不能一下子读入内存中位数 --> 需要遍历所有数据1. 题目在一个大文件中有100亿个32位整数,乱序排列,要求找出中位数;内存限制为512M;请写出算法设计思路;2. 基本知识b --> bit 比特,位(二进制中...

    前两天同学面试遇到的一道题,大数据怎么找出中位数。大数据 --> 所有数据不能一下子读入内存

    中位数 --> 需要遍历所有数据

    1. 题目

    在一个大文件中有100亿个32位整数,乱序排列,要求找出中位数;内存限制为512M;请写出算法设计思路;

    2. 基本知识b --> bit 比特,位(二进制中的位)

    B --> Byte 字节,1个字节=8位,1B=8bit

    K --> KB 千字节,1KB = 1024B

    M --> MB 兆字节,1M = 1024KB = 1024*1024B

    G --> GB 1GB = 1024MB

    问题分析:

    100亿个32位整数,那么占用内存为:100亿*32 b = 100亿*32/8 B = 100亿*32/8/1024 KB = 100亿*32/8/1024/1024 MB = 38146.9727 MB > 512M

    所以肯定不能一次全部载入内存。

    那么512M可以装多少个32位整数呢?512M=512*1024K=512*1024*1024B=512*1024*1024*8b=4294967296b

    可存储32位整数:4294967296b/32b=134217728个,是1亿多个。

    3. 设计思路

    中位数需要遍历数据才能找出来,512M是我们可用的内存限制,每次只能载入1亿多个数据,100亿个数据,我们可以分成100次进行载入遍历。

    那么遍历的时候我们需要记录哪些信息呢,因为要求中位数,我们可以考虑将数据划分区间,计算每个区间我们的数据落进去的数目,就可以大致知道数据分布了。

    所以整体的数据区间是多少呢?一个有符号的32位整数,他的取值范围是:

    ,总共有4294967296个整数值,我们把它划成100000组,那么每组的数据是43000个,最后一组少点。

    所以第一个区间

    的取值范围是

    ,第二个区间

    的取值范围是

    ,以此类推。

    我们分一百次载入数据,每次载入1亿个,遍历这一亿个数据,数据落入哪个区间,则那个区间的统计值+1。100次载入完成之后,我们便得到了每个区间的统计值,我们用

    来代表数据落入

    区间的统计值。那么肯定有

    现在我们需要寻找中位数落入哪个区间,100亿个数据的话,中位数应该等于第50亿大和第50亿+1大的数据的平均。所以我们需要知道这两个数,落在哪个区间了。

    我们从

    开始累加,一直累加到

    使得总和大于50亿+1,(等于的情况比较特殊,这里假设不等于。)那么关系到中位数计算的两个数应当落在区间

    里。

    到这里我们可以得到中位数的区间,我们计算一下上面的过程所需内存情况:载入1亿个数据需要内存32*1亿/8/1024/1024=381.5M,100000个数据组的统计值需要内存8*100000/1024/1024=0.76M 总和小于512,是可行的。

    现在我们知道涉及中位数计算的两位数的区间是

    ,而

    里有43000个数据,在

    之前的区间中的数据总数为

    ,下面我们需要找到第50亿个以及第50亿+1这两个数到底是多少。

    我们需要再次遍历数据,对落在

    区间的数都加以关注,统计

    区间内数字

    出现的频词

    再次分100次载入数据,对落在

    区间的数进行关注,为

    区间内的每一个整数

    都设计一个统计值

    区间有43000个数据,所以有43000个统计量。

    全部载入完成之后,我们再在

    的基础上累加

    ,如果加上

    大于50亿了,那么第50亿大的数据就是

    ,第50亿+1大的数同理。这样我们就可以计算中位数了。

    再次对上面的过程进行内存分析,每次载入1亿个数据需要内存381.5M,43000个统计量需要内存肯定小于上面100000个统计量的情况,所以内存也是足够的。哦还有个

    需要内存,但是8位,不值一提。

    设计思路以上。

    4. 总结数据量过大,分次载入

    一共需要两次完全遍历数据。

    5. 类似题目

    大量数据寻找topK,

    解决思路:数据多次载入,进行堆排序,每次载入都获得堆的topK,最后将这些topK再进行排序。

    加油,一件事竟然决定去做了,就请做好他。耐住性子,慢慢来。

    展开全文
  • 最朴素写法,每到奇数时位将前面所有数据排序,找到中位数 每次sort是o(nlogn)o(nlog_n)o(nlogn​),一组数据需要sort n2\frac{n}{2}2n​次,所以复杂度为o(n2logn)o(n^2log_n)o(n2logn​) #include <iostream>...

    问题描述

    依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出已读入的整数构成的序列的中位数。详细内容

    sort

    最朴素写法,每到奇数时位将前面所有数据排序,找到中位数
    每次sort是 o ( n l o g n ) o(nlog_n) o(nlogn),一组数据需要sort n 2 \frac{n}{2} 2n次,所以复杂度为 o ( n 2 l o g n ) o(n^2log_n) o(n2logn)

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int M = 10010;
    
    int a[M];
    
    int main()
    {
        int p;
        cin >> p;
        while (p --)
        {
            int n, m;
            cin >> n >> m;
            cout << n << ' ' << (m + 1 >> 1) << endl;
    
            int cnt = 0;
            for (int i = 1; i <= m; ++ i)
            {
                cin >> a[i];
                if (i % 2 != 0) 
                {
                    sort(a + 1, a + i + 1);
                    cout << a[1 + i >> 1] << ' ';
                    if (++ cnt % 10 == 0) cout << endl;   
                }
            }
    
            if (cnt % 10) cout << endl; // 解决最后一行数据不够10个,上方未输出空行,会导致下面组的格式出问题
        }
    
        return 0;
    }
    

    快速选择

    寻找中位数,上述做法采用 O ( n l o g n ) O(nlog_n) O(nlogn)的快速排序,也可以采用O(n)的快速选择
    所以每组的复杂度就变为了 o ( n 2 ) o(n^2) o(n2)

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int M = 10010;
    
    int a[M];
    
    int qsort(int a[], int l, int r, int k)
    {
        if (l >= r) return a[l];
    
        int x = a[l + r >> 1], i = l - 1, j = r + 1;
        while (i < j)
        {
            while (a[++ i] < x);
            while (a[-- j] > x);
            if (i < j) swap(a[i], a[j]);
        }
    
        int sl = j - l + 1;
        if (k <= sl) return qsort(a, l, j, k);
        return qsort(a, j + 1, r, k - sl);
    }
    int main()
    {
        int p;
        cin >> p;
        while (p --)
        {
            int n, m;
            cin >> n >> m;
            cout << n << ' ' << (m + 1 >> 1) << endl;
    
            int cnt = 0;
            for (int i = 1; i <= m; ++ i)
            {
                cin >> a[i];
                if (i % 2 != 0) 
                {
                    cout << qsort(a, 1, i, 1 + i >> 1) << ' ';
                    if (++ cnt % 10 == 0) cout << endl;   
                }
            }
    
            if (cnt % 10) cout << endl; // 解决最后一行数据不够10个,上方未输出空行,会导致下面组的格式出问题
        }
    
        return 0;
    }
    

    对顶堆

    应用场景
    维护动态区间的中位数

    小根堆始终维护大于大根堆堆顶元素的元素,大根堆维护小于大根堆堆顶元素的元素,由于我们保证大根堆元素个数最多比小根堆元素个数多1,所以在奇数个元素的情况下,两个堆的元素数量不会相等,一定是相差1,大根堆堆顶元素恰好位于中位数的位置,每个数据插入堆中是 O ( l o g n ) O(log_n) O(logn)的,所以每组的时间复杂度为 O ( n l o g n ) O(nlog_n) O(nlogn)
    之所以能够想到这样的解法,是因为我们确定中位数其实并不需要保证其左右两侧数据的有序性,只要左侧数据都小,右侧数据都大即可,使用两个堆恰好能够维护这种性质

    #include <iostream>
    #include <algorithm>
    #include <queue>
    
    using namespace std;
    
    int main()
    {
        int p;
        cin >> p;
        while (p --)
        {
            int n, m;
            cin >> n >> m;
            cout << n << ' ' << (m + 1 >> 1) << endl;
    
            priority_queue<int> down; // 下方的大根堆
            priority_queue<int, vector<int>, greater<int>> up; // 上方的小根堆
    
            int cnt = 0;
            for (int i = 1; i <= m; ++ i)
            {
                int x;
                cin >> x;
    
                if (down.empty() || x < down.top()) down.push(x); // 这里用<或<=均可,因为我们只需要保证中位数的相对位置正确即可
                else up.push(x);
    
                /**
                 * 这里只是规定了down的大小比size的大小大1,这样中位数一定在down中
                 * 如果没有明确规定哪个堆中数据更多,而只是保证两者大小的差的绝对值不大于1,那么中位数出现在size更大的堆中,
                 * 虽然这样同样可以得出结论,但由于没有明确规定中位数到底出现在哪里,所以代码实现时判断条件要更多一些
                 */
                if (down.size() > up.size() + 1) up.push(down.top()), down.pop();
                /**
                 * 按照理论,需要保证大根堆down的个数使用比小根堆的个数大一
                 * 即合法状态为up.size() = down.size() - 1,所以当up.size() >= down.size()时就需要调整,可这里没有=为什么也能ac
                 * 原因在于题目中规定了奇数个数时才会选择中位数,此时up和down中总数为奇数,两者size不可能相等,所以>时才调整也能保证到奇数个时能够得到正确结果
                 */
                if (up.size() >= down.size()) down.push(up.top()), up.pop();
    
                 if (i % 2 != 0) 
                {
                    cout << down.top() << ' ';
                    if (++ cnt % 10 == 0) cout << endl;   
                }
            }
    
            if (cnt % 10) cout << endl; // 解决最后一行数据不够10个,上方未输出空行,会导致下面组的格式出问题
        }
    
        return 0;
    }
    
    展开全文
  • 本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章。问题描述给定两个大小为m和n的有序数组nums1和nums2,找出这两个有序数组的中位数。示例一:nums1=[...

    本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章。

    问题描述

    给定两个大小为m和n的有序数组nums1和nums2,找出这两个有序数组的中位数。

    示例一:

    nums1=[1,3]

    nums2=[2]

    则中位数为2

    示例二:

    nums1=[1,2]

    nums2=[3,4]

    则中位数为(2+3)/ 2 = 2.5

    示例三:

    nums1 = [1,3,6,8]

    nums2 = [2,4,7,9,10,15]

    输出结果为6.5

    解决方案

    当两个有序数组的长度之和为奇数的时候,中位数只有一个。

    当两个有序数组的长度之和为偶数的时候,则中位数为数组合并之后位于中间的两个数的平均数。

    这里我们使用二分查找求取中位数

    187a3892d2abfaa3f0117f42eeed3019.png

    上面第一排是长度为偶数的情况,用红色的分隔线隔开,此时中位数有两个,一个是2,一个是3分别是分割线左边最大数和分割线右边最小数。

    第二排是长度为奇数的情况下,同样用红色的分隔线隔开,我们用分隔线隔开时让左边多一个元素,则分隔线左边这个元素就是长度为奇数的有序数组的中位数。(相应的,如果让右边多一个元素,则分隔线右边这个元素就是中位数,两种方式都可以)

    同样的,在两个有序数组的情况下,我们也可以做一个划分,把两个有序数组划分成左边部分和右边部分,需要注意的是位于分割线左边的元素个数和位于分割线右边的元素个数应该大致相等。

    f0716d2d24479e6f5aed1b0132e6791e.png

    1.如果两个有序数组的长度之和为偶数,则我们划分左边元素个数和右边元素个数是相等的。

    2.如果两个有序数组的长度之和为奇数,则我们划分左边元素个数比右边元素个数多一个。(同理:右边比左边多一个也是可以的)

    3.一个必须满足的条件:分隔线左边所有元素的数值<=分隔线右边所有元素数值。

    f5b80151247e0ef4427ec91404e925ca.png

    因为两个都是有序数组,这样一来,中位数就一定只与分隔线两边黄色标记的数有关了。

    当两个有序数组的长度之和为奇数时,我们让左边多一个元素

    7b17d820b508a5ae6d6eb1e631d7837c.png

    这样一来,左边最大的那个值就是两个有序数组合并之后的中位数了。

    同样情况,当两个有序数组的长度之和为偶数时,我们划分两边元数个数相等,当然,左边所有元素数值<=右边所有元素数值。

    f95c60c6c937ca13b4c38c15d05565cd.png

    如上图所示,黄色标记为合并之后数组的两个中位数,8就为左边元素最大值,而9为右边元素最小值

    假设数组1的长度为m,数组2的长度为n

    当m+n 为偶数时,则左边元素个数为(m+n)/2个

    当m+n 为奇数时,我们认为左边的元素个数比右边元素个数多一个,则左边元素个数为(m+n+1)/2个

    由于在编程语言中,除法是整数除法,而整数除法默认小数前的整数,所以当m+n为偶数时,让(m+n)/2 = (m+n+1)/2。

    这样我们就可以把偶数和奇数两种写法合并起来,左边元素个数就都为(m+n+1)/2。这样我们就不用对数组长度之和的奇偶性进行讨论,只需要确定一个数组划分的位置,然后通过关系计算出另一个数组划分的位置。

    注意:在满足划分的情况下,还有一个条件就是在左边第一排的最大值要小于右边第二排的最小值,同样,左边第二排的最大值也要小于右边第一排的最小值,这样交叉成立,划分才成立。

    解决这个问题

    还存在几种特殊情况

    第一种情况:较短的数组在分隔线右边没有元素。

    68f365129076608e3b8438993b2f8ba2.png

    870efeeaabbc2a70dc30881f599a0c60.png

    第二种情况:较短的数组在分隔线左边没有元素。

    解决办法:

    首先判断第一个数组和第二数组长度,为了保证较短的数组为第一个数组

    使用条件语句进行判断:if(nums1.length>nums.length){int[] nums3 = nums1; nums1 = nums2; nums2 = nums3}这样就实现了一个数组和第二个数组位置交换,

    定义第一个数组分隔线右边第一个元素的下标为i

    同样,定义第二个数组分隔线右边的第一个元素的小标为j

    根据之前的分析,i和j满足关系式:i+j=(m+n+1)/2,代表的是分隔线左边所有元素的个数。

    如果nums1数组较短,那么就应该在nums1[0,m]左闭右闭区间里查找恰当的分隔线。

    分隔线所要满足的条件就是:前面所写的(注意)

    定义left = 0

    Right = m

    分隔线左边所有元素需要满足的个数是:totalLeft = (m+n-1)/2

    i = left+(right-left)/2

    j = totalLeft – i

    利用循环的方法就能够找到分隔线的位置了。

    代码如下:

    Java寻找两个有序数组的中位数

    package Solutions;

    public class Solution {

        public double findMedianSortedArrays(){

            int[] nums1 = {1,3,6,8};

            int[] nums2 = {2,4,7,9,10,15};

            //为了使得分隔线在第二个数组的两侧都有元素,以至于不会出现访问时下标越界的情况

            if (nums1.length>nums2.length){

                //如果第一个数组长度大于第二个数组的长度

                //则将较短的那个数组设置成第一个数组

                int[] nums3 = nums1;

                nums1 = nums2;

                nums2 = nums3;

            }

            int m = nums1.length;

            int n = nums2.length;

            //分隔线左边的所有元素个数需要满足的个数 (m+n+1)/2

            int totalLeft = (m+n+1)/2;

            //在nums1的区间[0,m]里查找恰当的分割线,

            //使得nums1[i-1] <= nums2[j] &&nums[j-1] <= nums1[i]

            int left = 0;

            int right = m;

            while (left<right){

                int i = left+(right-left+1)/2;

                int j = totalLeft-i;

                if (nums1[i-1]>nums2[j]){

                    //下一轮搜索的区间[left,i-1]

                    right = i-1;

                }else {

                    //下一轮搜索的区间是[i,right]

                    left = i;

                }

            }

            int i = left;

            int j = totalLeft-i;

            int nums1LeftMax = i == 0? Integer.MIN_VALUE : nums1[i-1];

            int nums1RightMin = i == m? Integer.MAX_VALUE :nums1[i];

            int nums2LeftMax = j == 0? Integer.MIN_VALUE : nums2[j-1];

            int nums2RightMin = j == m? Integer.MAX_VALUE :nums2[j];

            if ((m+n)%2==1){

                return Math.max(nums1LeftMax,nums2LeftMax);

            }else {

                double a= (double) (Math.max(nums1LeftMax,nums2LeftMax)+Math.min(nums1RightMin,nums2RightMin))/2;

                System.out.println(a);

                return a;

            }

        }

        public static void main(String[] args) {

            Solution solution = new Solution();

            solution.findMedianSortedArrays( );

        }

    }

    结语

    本文章简要介绍使用二分查找方法解决两个有序数组的中位数,解决这个问题还可以使用先将两个数组进行合并,然后再求取中位数。

    实习编辑:王晓姣

    稿件来源:深度学习与文旅应用实验室(DLETA)

    展开全文
  • 转:海量数据中位数

    2020-12-28 21:24:22
    题目如下:只有2G内存的pc机,在一个存有10G个整数的文件,从中找到中位数,写一个算法算法:1.利用外排序的方法,进行排序 ,然后再去找中位数2.另外还有个思路利用堆先求第1G大,然后利用该元素求第2G大,然后...

    题目如下:

    只有2G内存的pc机,在一个存有10G个整数的文件,从中找到中位数,写一个算法。

    算法:

    1.利用外排序的方法,进行排序 ,然后再去找中位数

    2.另外还有个思路利用堆

    先求第1G大,然后利用该元素求第2G大,然后利用第2G大,求第3G大...当然这样的话虽不需排序,但是磁盘操作会比较多,具体还需要分析下与外排序的效率哪个的磁盘IO会比较多

    建立一个1g个整数的最大值堆,如果元素小于最大值则入堆,这样可以得到第1g大的那个元素然后利用这个元素,重新建一次堆,这次入堆的条件还要加上大于这个第1g大的元素,这样建完堆可以得到第2g大的那个 ...

    3.借鉴基数排序思想

    偶认为可以用位来判断计数,从最高位到最低位,为了方便表述我们假设为无符号整数,即0x00000000~0xFFFFFFFF依次递增,那么可以遍历所有数据,并记录最高位为0和1的个数(最高位为0的肯定是小于最高位为1的)记为N0、N1

    那么根据N0和N1的大小就可以知道中位数的最高位是0还是1

    假设N0>N1,那么再计算N00和N01,

    如果N00>(N01+N1),则说明中位数的最高两位是00

    再计算N000和N001.。。。依次计算就能找到中位数

    如果改进一下,设定多个计数器

    好像一次磁盘io也可以统计出N0,N00,....的数值

    4.借鉴桶排序思想

    一个整数假设是32位无符号数

    第一次扫描把0~2^32-1分成2^16个区间,记录每个区间的整数数目

    找出中位数具体所在区间65536*i~65536*(i+1)-1

    第二次扫描则可找出具体中位数数值

    第一次扫描已经找出中位数具体所在区间65536*i~65536*(i+1)-1

    然后第二次扫描再统计在该区间内每个数出现的次数,就可以了.

    题目:在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可(内存限制为 2G的意思就是,可以使用2G的空间来运行程序,而不考虑这台机器上的其他软件的占用内存)。分析:既然要找中位数,很简单就是排序的想法。那么基于字节的桶排序是一个可行的方法 :

    思想:将整形的每1byte作为一个关键字,也就是说一个整形可以拆成4个keys,而且最高位的keys越大,整数越大。如果高位keys相同,则比较次高位的keys。整个比较过程类似于字符串的字典序。

    第一步:把10G整数每2G读入一次内存,然后一次遍历这536,870,912个数据。每个数据用位运算">>"取出最高8位(31-24)。这8bits(0-255)最多表示255个桶,那么可以根据8bit的值来确定丢入第几个桶。最后把每个桶写入一个磁盘文件中,同时在内存中统计每个桶内数据的数量,自然这个数量只需要255个整形空间即可。

    代价:(1) 10G数据依次读入内存的IO代价(这个是无法避免的,CPU不能直接在磁盘上运算)。(2)在内存中遍历536,870,912个数据,这是一个O(n)的线性时间复杂度。(3)把255个桶写会到255个磁盘文件空间中,这个代价是额外的,也就是多付出一倍的10G数据转移的时间。

    第二步:根据内存中255个桶内的数量,计算中位数在第几个桶中。很显然,2,684,354,560个数中位数是第1,342,177,280个。假设前127个桶的数据量相加,发现少于1,342,177,280,把第128个桶数据量加上,大于1,342,177,280。说明,中位数必在磁盘的第128个桶中。而且在这个桶的第1,342,177,280-N(0-127)个数位上。N(0-127)表示前127个桶的数据量之和。然后把第128个文件中的整数读入内存。(平均而言,每个文件的大小估计在10G/128=80M左右,当然也不一定,但是超过2G的可能性很小)。

    代价:(1)循环计算255个桶中的数据量累加,需要O(M)的代价,其中m<255。(2)读入一个大概80M左右文件大小的IO代价。

    注意,变态的情况下,这个需要读入的第128号文件仍然大于2G,那么整个读入仍然可以按照第一步分批来进行读取。

    第三步:继续以内存中的整数的次高8bit进行桶排序(23-16)。过程和第一步相同,也是255个桶。

    第四步:一直下去,直到最低字节(7-0bit)的桶排序结束。我相信这个时候完全可以在内存中使用一次快排就可以了。

    整个过程的时间复杂度在O(n)的线性级别上(没有任何循环嵌套)。但主要时间消耗在第一步的第二次内存-磁盘数据交换上,即10G数据分255个文件写回磁盘上。一般而言,如果第二步过后,内存可以容纳下存在中位数的某一个文件的话,直接快排就可以了。

    更多:

    http://www.cppblog.com/richbirdandy/archive/2008/09/09/61426.html

    http://blog.csdn.net/randyjiawenjie/article/details/6968591

    http://kenby.iteye.com/blog/1030055

    题目介绍:

    输入为不断地数字流,实时显示出当前已经输入的数字序列的中位数

    解答:

    求中位数的方法很多,对于大数据量最经典是桶的计数方法,但是对于这个问题不适用,因为数据是不断变化的

    可以用最大堆和最小堆来解答这个问题:

    1.假设当前的中位数为m,其中最大堆维护的是<=m的数字序列,最小堆维护的是>=m的数字序列,但是两个堆都不包含m

    2.当新的数字到达时,比如为a,将a与m进行比较,若a<=m 则将其加入到最大堆中,否则将其加入到最小堆中

    3.如果此时最小堆和最大堆的元素个数的差值>=2 ,则将m加入到元素个数少的堆中,然后从元素个数多的堆将根节点赋值到m,最后重建两个最大堆和最小堆,返回到2。

    变形:

    题目:5亿个int,从中找出第k大的数

    #include #include#include#include#include#include#include#include#includetypedefstructbucket_t {int *buf; /*输出缓冲区*/

    int count; /*当前有多少个数*/

    int idx; /*缓冲区的指针*/} bucket_t;static unsigned int BUF_PAGES; /*缓冲区有多少个page*/

    static unsigned int PAGE_SIZE; /*page的大小*/

    static unsigned int BUF_SIZE; /*缓冲区的大小, BUF_SIZE = BUF_PAGES*PAGE_SIZE*/

    static unsigned int nbuckets; /*分成多少个桶*/

    static unsigned intBUCKET_BUF_SIZE;static int *buffer; /*输入缓冲区*/

    longget_time_usecs();void write_to_file(bucket_t *bucket, intpos);int partition(int *a, int s, intt);int quick_select(int *a, int s, int t, inti);void swap(int *p, int *q);int main(int argc, char **argv)

    {char filename[20];

    unsignedintbp, length, bucket_size, k;intfd, i, bytes;

    bucket_t*bucket;long start_usecs =get_time_usecs();

    strcpy(filename, argv[1]);

    fd=open(filename, O_RDONLY);if (fd < 0) {

    printf("can't open file %s\n", filename);

    exit(0);

    }

    nbuckets= 1024;

    k= atoi(argv[2]);

    PAGE_SIZE= 4096; /*page = 4KB*/BUF_PAGES= 1024;

    BUF_SIZE= PAGE_SIZE*BUF_PAGES; /*4KB * 1024 = 4M*/BUCKET_BUF_SIZE= PAGE_SIZE*128; /*4KB * 128 = 512KB*/buffer= (int *)malloc(BUF_SIZE);//把1-2^32个数分成nbucket个组, nbuckets必须等于2的n次幂

    bucket = malloc(sizeof(bucket_t)*nbuckets);if (bucket == NULL) exit(0);for (i = 0; i < nbuckets; i++) {

    bucket[i].buf=malloc(BUCKET_BUF_SIZE);if (bucket[i].buf ==NULL) {

    exit(0);

    }

    bucket[i].idx= 0;

    bucket[i].count= 0;

    }

    bucket_size= (1<<22); /*分成1024个桶,每个桶容纳2^22个数*/

    //读入第一批数据到输入缓冲区

    bytes =read(fd, buffer, BUF_SIZE);

    length= bytes/4;

    bp= 0;intelement, pos;

    unsignedint base;

    bucket_t*p;base = 2147483648;while (1) {//从输入缓冲区取出一个数,加到对应的桶

    element = buffer[bp++];

    pos= (((long)element)+base)>>22;

    p= &bucket[pos];

    p->buf[p->idx++] =element;

    p->count++;//桶内的缓冲区已满,写入文件

    if (p->idx*4 ==BUCKET_BUF_SIZE) {

    write_to_file(p, pos);

    p->idx = 0;

    }//输入缓冲区的数已用完

    if (bp ==length) {

    bytes=read(fd, buffer, BUF_SIZE);if (bytes == 0) {break;

    }

    length= bytes/4;

    bp= 0;

    }

    }//把每个桶剩下的数写入文件

    for (i = 0; i < nbuckets; i++) {

    write_to_file(bucket+i, i);

    }

    free(buffer);

    close(fd);

    buffer= malloc(bucket_size*4);if (buffer == NULL) exit(0);//找出第k大的数位于哪个文件

    unsigned sum = 0;for (i = 0; i < nbuckets && sum < k; i++) {

    sum+=bucket[i].count;

    }

    i--;//把该文件读入内存

    sprintf(filename, "foo_%d.dat", i);

    printf("第%d大的数位于文件%s的第%d大的数\n", k, filename, k+bucket[i].count-sum);

    fd=open(filename, O_RDONLY);if (fd < 0) {

    printf("can't open file %s\n", filename);

    free(buffer);

    exit(0);

    }

    bytes= read(fd, buffer, bucket_size*4);

    length= bytes/4;//选择文件内第(k+bucket[i].count-sum)大的数

    intanswer;

    answer= quick_select(buffer, 1, length-1, k+bucket[i].count-sum);

    printf("第%d大的数 = %d\n", k, answer);

    close(fd);

    free(buffer);//free buckets

    for (i = 0; i < nbuckets; i++) {

    free(bucket[i].buf);

    }

    free(bucket);long end_usecs =get_time_usecs();double secs = (double)(end_usecs - start_usecs) / (double)1000000;

    printf("it took %.02f seconds.\n", secs);return 0;

    }void write_to_file(bucket_t *bucket, intpos)

    {char filename[20];intfd, bytes;

    sprintf(filename,"foo_%d.dat", pos);

    fd= open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd < 0) {

    printf("can't open file %s\n", filename);

    exit(0);

    }

    bytes= write(fd, bucket->buf, bucket->idx*4);if (bucket->idx*4 !=bytes) {

    printf("idx = %d, bytes = %d, write error\n", bucket->idx, bytes);

    close(fd);

    exit(0);

    }

    close(fd);

    }longget_time_usecs()

    {structtimeval time;structtimezone tz;

    memset(&tz, '\0', sizeof(structtimezone));

    gettimeofday(&time, &tz);long usecs = time.tv_sec*1000000 +time.tv_usec;returnusecs;

    }void swap(int *p, int *q)

    {inttmp;

    tmp= *p;*p = *q;*q =tmp;

    }/*把a[t]作为参考,将数组分成三部分: 小于等于a[t],

    * a[t]以及大于a[t],分割完毕后,a[t]所在的下标即是a[t]的顺序*/

    int partition(int *a, int s, intt)

    {int i, j; /*i用来遍历a[s]...a[t-1], j指向大于x部分的第一个元素*/

    for (i = j = s; i < t; i++) {if (a[i]

    swap(a+i, a+j);

    j++;

    }

    }

    swap(a+j, a+t);returnj;

    }/*选择数组中第i大的元素并返回*/

    int quick_select(int *a, int s, int t, inti)

    {intp, m;if (s == t) returna[t];

    p=partition(a, s, t);

    m= p - s + 1;if (m == i) returna[p];if (m >i) {return quick_select(a, s, p-1, i);

    }return quick_select(a, p+1, t, i-m);

    }

    展开全文
  • 寻找两个正序数组的中位数 描述 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 示例 1: 输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:...
  • 中位数和众数

    2021-03-15 10:57:25
    列举一些中位数和众数的常见问题和解法1. 众数一个长度为$N$的列表,出现次数大于$\left \lfloor N/2 \right \rfloor$的数为这个列表的众数。1.1 摩尔投票算法摩尔投票算法(Boyer-Moore majority vote algorithm)的...
  • 480. 滑动窗口中位数 方法一 利用multiset 思路 代码 方法二 大顶堆小顶堆 维护平衡 思路 代码 480. 滑动窗口中位数 题目链接:https://leetcode-cn.com/problems/sliding-window-median/ 难度困难239 ...
  • 计算大数据的中位数

    2020-12-28 21:24:25
    题目:在一个大文件中有10G个整数,乱序排列,要求找出中位数(内存有2G限制,不能一次全部加装),请写出算法设计思路。中位数的定义:对于一个排序好的序列,如果数据有奇数个的话,中位数就取中间的一个;如果有...
  • 【经典算法题】寻找两个正序数组的中位数 Leetcode 0004 寻找两个正序数组的中位数 题目描述:Leetcode 0004 寻找两个正序数组的中位数 分析 本题的考点:二分。 这里将nums1、nums2分别记为A、B。对于给定...
  • 文章目录数据结构基本定义和类型八大数据结构概述数组(Array)...Tree)树的数据结构实现二叉搜索树(BST)平衡二叉树和红黑树红黑树B-treeB+树区间树(线段树):STLset的实现图(Graph)图的遍历图的拓扑排序图的最小生成树
  • 数据结构与算法必知基础知识

    千次阅读 多人点赞 2021-01-06 22:58:12
    数据结构与算法是程序员内功体现的重要标准之一,且数据结构也应用在各个方面,业界更有程序=数据结构+算法这个等式存在。各个中间件开发者,架构师他们都在努力的优化中间件、项目结构以及算法提高运行效率和降低...
  • 数据结构的排序算法基本概念 基本概念 通常需要熟练掌握快速排序和归并排序 要熟练掌握各种排序算法之间的优缺点,各种算法的思想以及各种算法的使用场景,还要熟练分析各种算法之间的时间复杂度和空间复杂度 排序:...
  • 1.算法题一:无序数组的中位数 (快排思想O(N) 时间复杂度) package com.lightsword.leetcodeproblems import org.junit.jupiter.api.Test import java.util.* /** * 1.算法题一:无序数组的中位数 (快排思想O(N) ...
  • 如果数字的个数是奇数则直接返回中间的那个数,如果数字的个数是偶数此时这组数据中位数有两个,取中间两个数的平均值即可。想法一、不论用什仫排序算法使得该组数据有序,直接取中间值即可。这种只要你掌握常见的...
  • 30 个重要数据结构和算法完整介绍(建议收藏保存)

    万次阅读 多人点赞 2021-06-07 08:02:06
    数据结构和算法 (DSA)通常被认为...它包括 15 个最有用的数据结构和 15 个最重要的算法,可以帮助您在学习中和面试取得好成绩并提高您的编程竞争力。后面等我还会继续对这些数据结构和算法进行进一步详细地研究讲解。
  • 题目描述:560和为K的子数组给定一个整数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。示例 1 :输入:nums=[1,1,1],k=2输出:2说明: [1,1] 与 [1,1] 为两种不同的情况。示例2:输入:nums=[1,1,1...
  • 数位DP是与数位相关的一类计数类DP,一般用于统计[l,r]区间满足特定条件的元素个数位指个,十,百位等,数位DP就是在数位上进行动态规划。数位DP在实质上是一种有策略的穷举方式,在子问题求解完毕后将其...
  • 数值方法样本统计量:数据来自样本,计算的度量总体参数:数据来自总体,计算的度量点估计...公式为: 中位数将所有数据按升序排序后,位于中间的数值即为中位数。 (1)当观测值是奇数时,中位数就是中间那个数值。 (...
  • 数据挖掘相关算法

    2021-08-23 16:12:29
    数据获取1.1 数据挖掘的对象1.2数据挖掘的步骤1.3支持数据挖掘的关键技术1.4数据仓库1.5数据仓库的模型1.6典型的OLAP操作2 数据准备2.1 维归约/特征提取2.1.1决策树归约2.1.2粗糙集归约2.2 数据变换2.2.1归一化与...
  • 来进行数字的添加和中位数的查找。 public void addNum(int num) public double findMedian() 思路:1.先说一下基本的思路: 可以对目前的数据进行排序,如使用快速排序O(nlogn),然后直接返回中间的那个数O(1)。 2....
  • 一些经典的数据结构和算法图书,偏重理论,读者学起来可能感觉比较枯燥。一些趣谈类的数据结构和算法图书,虽然容易读懂,但往往内容不够全面。另外,很多数据结构和算法图书缺少真实的开发场景,读者很难将理论和...
  • 这系列相关博客,参考 数据结构与算法之美 如果想了解更多内容,请去这个博客 数据结构与算法之美 - 目录 数据结构与算法之美 - 37 | 贪心算法:如何用贪心算法实现Huffman压缩编码?如何理解“贪心算法”?贪心算法...
  • 295. 数据流的中位数【优先队列】 中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。 例如, [2,3,4] 的中位数是 3 [2,3] 的中位数是 (2 + 3) / 2 = 2.5 设计一个支持以下两种操作的...
  • 清华大学-数据挖掘理论与算法(国家级精品课)by 袁博老师 《数据挖掘概念与技术》
  • 295. 数据流的中位数题目描述解题思路 题目描述 中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。 例如, [2,3,4] 的中位数是 3 [2,3] 的中位数是 (2 + 3) / 2 = 2.5 设计一个...
  • 它是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。算法的核心概念...
  • 本文主要介绍数据结构与算法的知识点,包括线性表、数组和广义表、栈和队列、树和二叉树、图、查找、排序等,如有不当之处,敬请指正,感谢各位大佬!有些问题不免解释不全面或者暂时未回答(先留坑),欢迎各位读者...
  • 27、课程:贪心算法.7、以最低的成本连接绳索.mp4 ├─27、课程:贪心算法.8、最小平台.mp4 ├─27、课程:贪心算法.9、部分背包问题、分蛋糕.mp4 ├─28、课程:操作.10、下一个2的幂、计算一个整数的符号....
  • 伪随机数算法

    2021-02-12 22:51:30
    计算机的随机数都是由程序算法,也就是某些公式函数生成的,只不过对于同一随机种子与函数,得到的随机数列是一定的,因此得到的随机数可预测且有周期,不能算是真正的随机数,因此称为伪随机(Pseudo Random ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 57,150
精华内容 22,860
关键字:

区间数据中位数算法