算法通过村第十五关-超大规模|青铜笔记|海量找数


前言


提示:并不是所有黑暗的地方,都需要光明。 --珍妮特·温特森《句子不是唯一的水果》

在大部分算法中,默认给点给的数据量都是很小的,例如只有几个或者十几个元素,但是如果遇到了相当大的数据量高达百万乃至十亿,那么处理逻辑就会发生很大差异,也就是说算法中常考的,这个很重要。

这里的题目重点是理解怎么处理,面试的时候遇上可以不用慌张,做到心中有数,这一半也不会写代码。这里做如下演示:

在海量数据中,此时普通的数组、链表、Hash、树等等结构这里就没有什么效果了,因为内存空间肯定是放不下的。而常规的递归、排序、回溯、贪心甚至动态规划等思想在大量数据面前也是不顶用的。因为执行超时,必然要另寻他法。这类问题我们要如何下手呢?这里又三种比较今典的思路:

  1. 使用位存储,使用存储最大的好处是占用空间是简单存储整数的 1/8 。例如一个 40亿的整数数组,如果用整数存储需要 16GB 左右的空间,而如果使用位存储,就可以仅用 0.5GB 的空间,这样很多问题就能够解决了。
  2. 如果文件实在太大,无法在能存中存放,则需要考虑将大文件分成若干小块,先处理每块的,最后支部得到想要的结果,这种方式也叫做 外部排序。 这样需要遍历全部遍历至少两次,是经典的用时间换空间的方法。
  3. 。 在处理超大数据中找第K大,第K小,K个最大,K个最小。则特别使用堆来做。而且将超大数据换成流数据也是可以的,而且几乎是唯一的方式,口诀就是“查小用大堆,查大用小堆”。

用4KB内存寻找重复数

题目要求:给定一个数组,包含1到N的整数,N最大为32_000,数组可能还有重复值,且N的值取值不定,若只有4KB的内存可用,该如何打印数组中所有重复的元素。

分析:本身是一道海量数据问题的热身题目,如果去掉只用“4KB”的要求,我们可以先创建一个大小为N的数组,然后将这些数据放进去,但是整数最大为32_000。如果直接才用数组,则需要使用32_000 * 4B = 128KB的空间,而题目只有4kb 的内存限制,我们就必须先解决该如何存放的问题。

如果是只有4KB,那么考虑寻值,只能有 8 * 4 * 2 ^10 个比特。这个值要比32_000要大的多,因此我们可以创建一个32_000比特的维向量(比特数组),其中一个比特位位置就代表一个整数。利用这个位相量,就可以遍历整个数组,如果返现数组元素是v 那么将这个位置的v设置为1,碰到重复元素,就输出一下。

  /**
     * 检查重复项
     * @param array
     */
    public void checkDuplicates(int[] array){
        BitSet bs = new BitSet(32_000);
        for (int i = 0; i < array.length; i++) {
            int num = array[i];
            int num0 = num - 1;
            if(bs.get(num0)){
                System.out.println(num);
            }else{
                bs.set(num0);
            }
        }
    }
    class BitSet {
        int[] bitSet;
        public BitSet(int size){
            // 做数据压缩
            this.bitSet = new int[size >> 5];
        }

        public boolean get(int pos){
            int wordNumber = (pos >> 5); // 除以32
            int bitNumber = (pos & 0x1F); // 除以32
            return (bitSet[bitNumber] & (1 << bitNumber)) != 0;
        }

        public void set(int pos){
            int wordNumber = (pos >> 5); // 除以32
            int bitNumber = (pos & 0x1F); // 除以32
            bitSet[wordNumber] |= 1 << bitNumber;
        }
    }

这里重点理解下get和set:

用于从位集合(bitSet)中获取指定位置(pos)的位值。

具体的步骤如下:

  1. 首先,使用右移运算符 “>>” 对 pos 进行位移操作,将其除以32(实际上是用来确定位所在的字(word)的索引)。位移运算符 “>>” 的作用是将二进制数向右移动指定的位数,相当于进行除法运算。

  2. 然后,使用位与运算符 “&” 对 pos 进行按位与操作,与操作数 0x1F(16进制数,实际上是二进制的 00011111)进行按位与操作。这样能得到 pos 最后5位的值(实际上是用来确定位在字(word)内的具体位置的索引)。

  3. 接下来,通过使用按位与运算符 “&” 将位集合(bitSet)中的对应字(word)与 1 左移 bitNumber 位的数进行按位与操作。这样能够提取出位所在位置的值。

  4. 最后,使用不等于运算符 “!=” 对上一步提取的位值进行判断,如果不等于0,则返回 true,表示位集合中的对应位值为1;否则返回 false,表示位集合中的对应位值为0。

例如,如果想要获取位集合中第37位的值,经过运算后得到的 wordNumber 值为1,bitNumber 值为5。然后将 bitSet[bitNumber](位集合中的第5个字)与一个左移了 5 位的数值进行按位与操作,提取出第37位的值,并返回结果。

这段代码可以用于实现位图等数据结构的查询操作,在处理大量二进制位的场景中非常有用。

用来在一个位集合(bitSet)中设置指定位置(pos)的位为1。

具体的步骤如下:

  1. 首先,使用右移运算符 “>>” 对 pos 进行位移操作,将其除以32(实际上是用来确定位所在的字(word)的索引)。位移运算符 “>>” 的作用是将二进制数向右移动指定的位数,相当于进行除法运算。

  2. 然后,使用位与运算符 “&” 对 pos 进行按位与操作,与操作数 0x1F(16进制数,实际上是二进制的 00011111)进行按位与操作。这样能得到 pos 最后5位的值(实际上是用来确定位在字(word)内的具体位置的索引)。

  3. 最后,根据字(word)的索引和位位置的索引,将位集合(bitSet)中对应位置的值设置为1。使用按位或运算符 “|” 对位集合中的对应元素进行按位或操作,将位设置为1。

例如,如果传入的 pos 值为37,经过运算后得到的 wordNumber 值为1,bitNumber 值为5。然后将 bitSet[1] 中的对应位设置为1,表示位集合中的第37位被设置为1。

这段代码可以用于实现位图等数据结构的实现,在处理大量二进制位的场景中非常有用。


总结

提示:海量数据去重;大数据筛选;海量找数;流数据过滤;堆的应用

在这里插入图片描述


如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~

也欢迎你 关注我 ,喜欢交朋友,喜欢一起探讨问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

师晓峰

啤酒饮料矿泉水,你的打赏冲一冲

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值