面试题:如何在4亿个整数中快速判断某个整数是否存在

面试题:如何在4亿个正整数中快速判断某个整数是否存在,假设所有的整数都是4字节整型,可用的内存1G。

这个问题有几种思路:

  1. 顺序查找目标整数。因为可用内存只有1G,没法将4亿个整数全部加载到内存中查找,只能先将数据保存到文件中,然后顺序读取文件直到找到目标整数。时间复杂度为O(n).而且磁盘的IO时间为毫秒级,所以顺序读取完全部数据所需时间相当可观,实际测试需要1分钟左右。性能上不能满足题目要求。
  2. Hashmap实现。时间复杂度O(1),如果没有内存限制的前提下这种方法能够满足要求。可是现在内存限制1G,Hashmap没有足够的空间装下所有数据,所以此方法不可行。
  3. 位图实现。题设给出的是一堆4字节整数,除了用Integer来表示某个整数以外,其实我们还能用二进制位来表示。比如我们要用二进制位表示1,2,4,8 如下,分别将二进制的第1,2,4,8位置为1:

         总共才只需1个字节大小,比起用Integer表示节省了15个字节。 1G内存足够存储题设的所有整数。而且位图的查找高效,时间复杂度为O(1).

        使用JAVA提供的BitSet类可以容易地实现位图计算。下面的例子将1到4亿的整数保存在文件中,然后从文件读取到BitSet然后判断某个数是否存在。

  • 将1到4亿的数字保存到文件中:
    @Test
    public void writeManyIntegers(){
       try(PrintWriter printWriter =  new PrintWriter(Files.newBufferedWriter(Paths.get("D:\\tmp\\many_integers.txt")))){
           for(long i = 1; i<= 400000000; i++){
               printWriter.println(i);
           }
       } catch (IOException e) {
           throw new RuntimeException(e);
       }
    }
  • 加载到BitSet中并测试:
    @Test
    public void testBitSet() throws IOException {
        BitSet bitSet = new BitSet();
        Files.lines(Paths.get("D:\\tmp\\many_integers.txt")).forEach(line-> bitSet.set(Integer.parseInt(line)));
        System.out.printf("存在 0 ?%s\n",bitSet.get(0));
        System.out.printf("存在 1 ?%s\n",bitSet.get(1));
        System.out.printf("存在 400000000 ?%s\n",bitSet.get(400000000));
        System.out.printf("存在 400000001 ?%s\n",bitSet.get(400000001));
        System.out.printf("数据占用的总大小:%dm\n",bitSet.size()/8/1024/1024);
    }
  • 运行结果:

 

BitSet只支持4字节整数的操作,如果数据量更大,可以考虑将数据合理拆分到多个BitSet中。

需要注意的是:

  • 位图通常是用于大数据量较分散的数字表示,当只有少量数据的时候可能不是最佳的选择。比如只将一个较大的整数如 10000 保存在位图中,则需要创建一个至少为10000 bits的位图,比起用常规的Integer表示反而浪费了更多的内存空间。
  • BitSet不是线程安全的,并发场景下需要做好控制。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值