微信搜索【程序员囧辉】,关注这个坚持分享技术干货的程序员。

题目

最近看到一个题目:给40亿个不重复的 unsigned int 的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?

解法

搜了一下资料,该题目是腾讯的一道面试题,目前网上普遍给出的答案有两种。

1.《编程珠玑》给出的方案

我们把40亿个数中的每一个用32位的二进制来表示,假设这40亿个数开始放在一个文件中。

然后将这40亿个数分成两类:1.最高位为0;2.最高位为1。

并将这两类分别写入到两个文件中,其中一个文件中数的个数<=20亿,而另一个>=20亿(这相当于折半了);

与要查找的数的最高位比较并接着进入相应的文件再查找

再然后把这个文件为又分成两类:1.次最高位为0;2.次最高位为1。

并将这两类分别写入到两个文件中,其中一个文件中数的个数<=10亿,而另一个>=10亿(这相当于折半了);与要查找的数的次最高位比较并接着进入相应的文件再查找。

.......

以此类推,就可以找到了,而且时间复杂度为O(logn)。

此方案不是本文要讲的重点,只是把思路放在这边供大家参考。

2.位图法

思路

我们之所以无法将40亿个数字直接读取到内存去进行处理,是因为40亿个 unsigned int 的整数大约要占15G内存,正常情况下,没有这么大的内存,也不可能这样做。

这种情况是因为一个整数占用了4个字节(Byte),而在本题中,我们其实只关心某个数字是否存在,在这种情况下,我们可以通过位图法来解决该问题。

位图法思想:对于40亿个 unsigned int 的整数,每个数字用1个二进制数(一个二进制数占用1Bit,1Byte = 8Bit)来表示该数字是否存在,0为不存在,1为存在。从低位开始数:

第1个二进制数表示整数0是否存在,

第2个二进制数表示整数1是否存在,

第3个二进制数表示整数2是否存在,

依次类推 ... ...

第4294967296个二进制数用于表示整数4294967295是否存在。

unsigned int 在32&64位编译器的范围为 0~4294967295,4294967296个二进制数大约占用512M内存,是一个可以接受的范围。

例子

题目:我们读取了3个整数:1、2、4,要判断整数3是否被读取过了。

解答过程:

1)对于40亿个 unsigned int 的整数,我们可以定义4294967296个二进制数,并且全部初始化值为0。

2)读取整数1:将第2个二进制数赋值为1,表示整数1是存在的,此时值为:10(高位还有4294967294个0,为了方便阅读不写出来,下文同)

3)读取整数2:将第3个二进制数赋值为1,表示整数2是存在的,此时值为:110

4)读取整数4:将第5个二进制数赋值为1,表示整数4是存在的,此时值为:1 0110

5)判断整数3是否被读取过,因为第1个二进制数表示整数0是否存在,因此整数3则通过第4个二进制数表示,此时的值为:1 0110,第4个二进制数为0,所以得出结论:整数3没有被读取过。

代码实现

由于Java中无法直接操作二进制数,因此我们可以通过 int 来实现。1个二进制数占用1 Bit;1个 int 占用4 Byte,也就是32 Bit。因此,我们可以使用1个int来表示32个二进制数。

所以,我们有以下思路:

第1个int表示:整数0 ~ 31是否存在,

第2个int表示:整数32 ~ 63是否存在,

第3个int表示:整数64 ~ 95是否存在,依此类推。

因此,我们最终可以使用一个int数组来表示4294967296个二进制数,通过数组的下标来指示第几个int。

代码如下

package com.joonwhee.open.leetcode.temp;/*** 位图法** @author joonwhee* @date 2019/1/22*/
public class BitMap {/*** 位图提供的最大长度,* 比如unsigned int的最大值为4294967295, 则需要的length为4294967296*/private long length;/*** 位图桶*/private static int[] bitmapBucket;/*** int用来表示32位二进制数,* BIT_VALUE[0]表示第1个二进制数存在、* BIT_VALUE[1]表示第2个二进制数存在,以此类推** BIT_VALUE[0] = 00000000 00000000 00000000 00000001* BIT_VALUE[1] = 00000000 00000000 00000000 00000010* BIT_VALUE[2] = 00000000 00000000 00000000 00000100* ...* BIT_VALUE[31] = 10000000 00000000 00000000 00000000*/private static final int[] BIT_VALUE = {0x00000001, 0x00000002, 0x00000004, 0x00000008,0x00000010, 0x00000020, 0x00000040, 0x00000080,0x00000100, 0x00000200, 0x00000400, 0x00000800,0x00001000, 0x00002000, 0x00004000, 0x00008000,0x00010000, 0x00020000, 0x00040000, 0x00080000,0x00100000, 0x00200000, 0x00400000, 0x00800000,0x01000000, 0x02000000, 0x04000000, 0x08000000,0x10000000, 0x20000000, 0x40000000, 0x80000000};/*** length为1 - 32: 需要1个桶* length为33 - 64: 需要2个桶* ...* 以此类推** @param length*/public BitMap(long length) {this.length = length;// 根据长度算出,所需位图桶个数bitmapBucket = new int[(int) (length >> 5) + ((length & 31) > 0 ? 1 : 0)];}/*** 查找number是否存在于位图桶中** @param number 要查询的数字* @return true: number在位图桶中, false: number不在位图桶中*/public boolean getBit(long number) {if (number < 0 || number > length) {throw new IllegalArgumentException("非法参数");}// 计算该number在哪个桶int belowIndex = (int) (number >> 5);// 求出该number在桶里的下标,(求出该值在32位中的哪一位, 下标0 - 31)int offset = (int) (number & 31);// 拿到该桶的值int currentValue = bitmapBucket[belowIndex];// 计算该number是否存在return ((currentValue & BIT_VALUE[offset])) == 0 ? false : true;}/*** 将number在位图桶中标记为存在** @param number 要标记的数字*/public void setBit(long number) {// 合法性校验if (number < 0 || number >= length) {throw new IllegalArgumentException("非法参数");}// 计算该number在哪个桶int belowIndex = (int) (number >> 5);// 求出该number在桶里的下标,(求出该值在32位中的哪一位, 下标0 - 31)int offset = (int) (number & 31);// 拿到该桶的当前值int currentValue = bitmapBucket[belowIndex];// 将number在桶里标记bitmapBucket[belowIndex] = currentValue | BIT_VALUE[offset];}public static void main(String[] args) {BitMap bitMap = new BitMap(4294967296L);bitMap.setBit(4294967295L);System.out.println(bitMap.getBit(4294967295L));System.out.println(bitMap.getBit(4294967294L));}
}

了解了思路后,代码就比较简单了。

1)通过构造函数传的 length 值,构建一个足够大的 int数组 bitmapBucket,对于题目的unsigned int 的整数,length为4294967296刚好够用。

2)将40亿个给定的数通过 setBit 方法,将对应的位置的二进制数标记为1(1代表存在)。

3)通过 getBit 方法判断给定的 number 是否存在于之前的40亿个数中。

setBit 例子:例如我们要将整数 32 标记为存在。

1)首先计算出整数 32 所在的位图桶下标为1,也就是bitmapBucket[1]。

2)接着计算出整数 32 在bitmapBucket[1] 桶中的下标0(下标0代表该桶的第1个二进制数)。

3)拿到 bitmapBucket[1] 当前值 currentValue。

4)最后我们要将 bitmapBucket[1] 桶中第1个二进制数标记为1,并且不影响之前已经标记的二进制数,因此将 currentValue 与0000 0000 0000 0000 0000 0000 0000 0001 进行 ‘或’ 运算即可。而对于每一个二进制数,我们通过 BIT_VALUE来表示,这边的 0000 0000 0000 0000 0000 0000 0000 0001 ,可以通过 BIT_VALUE[0] 来表示。

位图法:判断一个数是否在40亿个整数中?相关推荐

  1. 漫画:如何判断一个数是否在40亿个整数中?

    文章来源于:https://wwww.iamshuaidi.com,一个专注于校招,面试,面经的编程网站 题目:我有40亿个整数,再给一个新的整数,我需要判断新的整数是否在40亿个整数中,你会怎么做? ...

  2. 如何判断一个数是否在40亿个整数中?

    来自:互联网侦察 小史是一个应届生,虽然学的是电子专业,但是自己业余时间看了很多互联网与编程方面的书,一心想进BAT. 今天他就去BAT中的一家面试了. 简单的自我介绍后,面试官给了小史一个问题. [ ...

  3. 「BAT面试现场」如何判断一个数是否在40亿个整数中?

    https://www.toutiao.com/a6699639753467232771/ 作者:channingbreeze 来自:公众号互联网侦察 小史是一个应届生,虽然学的是电子专业,但是自己业 ...

  4. 【BAT面试现场】如何判断一个数是否在40亿个整数中?

    作者 channingbreeze 如需转载,请联系原作者授权. 小史是一个应届生,虽然学的是电子专业,但是自己业余时间看了很多互联网与编程方面的书,一心想进BAT. 今天他就去BAT中的一家面试了. ...

  5. 【面试现场】如何判断一个数是否在40亿个整数中?

    小史是一个应届生,虽然学的是电子专业,但是自己业余时间看了很多互联网与编程方面的书,一心想进BAT. 今天他就去BAT中的一家面试了. 简单的自我介绍后,面试官给了小史一个问题. [面试现场] 题目: ...

  6. 利剑无意之如何判断一个数在40亿个整数中

    如何判断一个数在40亿个整数中 首先思路:用一个set存储就好了,整数32位,一个整数4个字节,40亿个整数,应该是160亿个字节,大概16GB. 此刻问题又来了,我的机器只有2GB内存,但是需要尽可 ...

  7. 【面试被虐】如何只用2GB内存从20亿,40亿,80亿个整数中找到出现次数最多的数?...

    这几天小秋去面试了,不过最近小秋学习了不少和位算法相关文章,例如 [面试现场]如何判断一个数是否在40亿个整数中? [算法技巧]位运算装逼指南 对于算法题还是有点信心的,,,,于是,发现了如下对话. ...

  8. 【面试被虐】如何只用2GB内存从20亿,40亿,80亿个整数中找到出现次数最多的数?

    这几天小秋去面试了,不过最近小秋学习了不少和位算法相关文章,例如 [面试现场]如何判断一个数是否在40亿个整数中? [算法技巧]位运算装逼指南 对于算法题还是有点信心的,,,,于是,发现了如下对话. ...

  9. 如何只用2GB内存从20/40/80亿个整数中找到出现次数最多的数

    来源:公众号[苦逼的码农] 这几天小秋去面试了,不过最近小秋学习了不少和位算法相关文章,例如: [算法技巧]位运算装逼指南 对于算法题还是有点信心的,,,,于是,发现了如下对话. 20亿级别 面试官: ...

最新文章

  1. stk 坐标系_STK学习-坐标系
  2. 基于Debian9.3安装OpenVAS9.0(kali源)
  3. 福利好礼现金大奖等你来→首届 .NET Conf China Hackathon 火热报名中!
  4. 新疆大学ACM新生赛(公开赛)
  5. Spring IO platform
  6. 互联网晚报 | 3月2日 星期三 |​ ​最高法:电商不得以商品已拆封为由拒绝七日无理由退货;小米投资纽迪瑞...
  7. MySQL:定时任务被拒绝
  8. Redis源码分析系列三:initServerConfig下半部分
  9. 如何花式计算20的阶乘?
  10. 软考资料(系统集成管理工程师)无偿分享
  11. 辽宁省全国计算机 考点 社会,辽宁2018年9月全国计算机二级考试报考地点
  12. 用Python模拟QQ界面之QQ登录界面的奥秘
  13. 云打码的简单使用举例
  14. 负数求余简单技巧(C语言)
  15. 灰色 GM(1,1)模型在重庆商品房销售价格预测中的应用
  16. 脚本---perl与python的比较
  17. js实现1-100以内的质数
  18. luogu 1351
  19. 武装我的“超强小黑”Thinkpad T400
  20. css 滚动条样式修改以及动态显示

热门文章

  1. Java反射机制的学习(3)
  2. 关于3B大战那点事儿
  3. 开荒手册3——构思一篇小论文
  4. python获取函数名、类名
  5. 大专前端实习生如何挣到月薪 20k
  6. Cause: java.sql.SQLException: connection closed问题排查、解决
  7. 用Qt实现图片的负片效果
  8. OpenCV入门——基本图像操作
  9. 卷毛0基础学习Golang-并发编程,01 什么是并发
  10. 2022-2027年中国大米蛋白粉行业市场全景评估及发展战略规划报告