一次搞透,面试中的数1问题的五种方法!

开发 开发工具
面试中,除了TopK,是否被问过:求一个正整数的二进制表示包含多少个1?到底有几种方法,这些思路里蕴含的优化思路究竟是怎么样的,今天和大家聊一聊。

面试中,除了TopK,是否被问过:求一个正整数的二进制表示包含多少个1?

画外音:姊妹篇《一次搞透,面试中的TopK问题!》。

[[443193]]

例如:

  1. uint32_t i=58585858

i的二进制表示是:

  1. 0000 0011 0111 1101 1111 0011 0000 0010 

于是,i的二进制表示包含15个1。

到底有几种方法,这些思路里蕴含的优化思路究竟是怎么样的,今天和大家聊一聊。

一、位移法。

思路:既然输入n是uint32,每次取n的最低位,判断是不是1,位移32次,循环判断即可。

伪代码:

  1. do{ 
  2.     if ((n&1)==1){ 
  3.        result++; 
  4.     } 
  5.     n>>= 1; 
  6.     i++; 
  7. } while(i<32); 

分析:不管n的二进制表示里包含多少个1,都需要循环计算32次,比较耗时。有没有可能,每次消除掉一个1,这样来降低计算次数呢?

二、求与法。

观察一下n与n-1这两个数的二进制表示:

(1)最末位一个1会变成0;

(2)最末位一个1之后的0会全部变成1;

(3)其他位相同;

栗子:

  1.            x = 1011 0000 
  2.          x-11010 1111 
  3. x & (x-1) = 1010 0000 

于是,n&(n-1)这个操作,可以起到“消除最后一个1”的功效。

思路:逐步通过n&(n-1),来消除n末尾的1,消除了多少次,就有多少个1。

伪代码:

  1. while(n){ 
  2.    result++; 
  3.    n&=(n-1); 
  4.   

分析:这个方法,n的二进制表示有多少个1,就会计算多少次。总的来说,n的长度是32bit,如果n的值选取完全随机,平均期望由16个1构成,平均下来16次,节省一半的计算量。

三、查表法。

空间换时间,是算法优化中最常见的手段,如果有相对充裕的内存,可以有更快的算法。

思路:一个uint32的正整数n,一旦n的值确定,n的二进制表示中包含多少个1也就确定了,理论上无需重新计算:

  • 1的二进制表示中包含1个1
  • 2的二进制表示中包含1个1
  • 3的二进制表示中包含2个1
  • 58585858的二进制表示中包含15个1
  • ...

提前计算好结果数组:

  1. result[1]=1; 
  2. result[2]=1; 
  3. result[3]=2; 
  4. … 
  5. result[58585858]=15; 
  6. … 

伪代码:

  1. return result[n]; 

查表法的好处是,时间复杂度为O(1),潜在的问题是,需要很大的内存。

内存分析:

  • 假如被分析的整数是uint32,打表数组需要记录2^32个正整数的结果。
  • n的二进制表示最多包含32个1,存储结果的计数,使用5个bit即可。
  • 故,共需要内存2^32 * 5bit = 2.5GB。

画外音:5个bit,能表示00000-11111这32个数。

四、二次查表法。

查表法,非常快,只查询一次,但消耗内存太大,在工程中几乎不被使用。

算法设计,本身是一个时间复杂度与空间复杂度的折衷,增加计算次数,往往能够减少存储空间。

思路:

(1)把uint32的正整数n,分解为低16位正整数n1,和高16正整数n2;

(2)n1查一次表,其二进制表示包含a个1;

(3)n2查一次表,其二进制表示包含b个1;

(4)则,n的二进制表示包含a+b个1;

伪代码:

  1. uint16 nn1 = n & 0xFFFF; 
  2. uint16 n2 = (n>>16) & 0xFFFF; 
  3. return  result[n1]+result[n2]; 

问题来了:增加了一倍的计算量(1次查表变2次查表),内存空间是不是对应减少一半呢?

内存分析:

  • 被分析的整数变成uint16,打表数组需要记录2^16个正整数的结果。
  • n1和n2的二进制表示最多包含16个1,存储结果的计数,使用4个bit即可。
  • 故,共需要内存2^16 * 4bit = 32KB。

好神奇!!!

计算量多了1次,内存占用量却由2.5G降到了32K(1万多倍),是不是很有意思?

五、总结

数1,不难;但其思路有优化过程,并不简单:

(1)位移法,32次计算;

(2)n&(n-1),能消除一个1,平均16次计算;

(3)查表法,1次查表,2.5G内存;

(4)二次查表法,2次查表,32K内存;

知其然,知其所以然。

思路比结论重要。

【本文为51CTO专栏作者“58沈剑”原创稿件,转载请联系原作者】

戳这里,看该作者更多好文 

 

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2021-12-20 10:39:30

TopK排序代码

2021-11-02 07:54:40

List分片Java

2022-09-15 14:05:02

ES开源

2022-12-29 08:46:15

IT采购投资

2021-08-27 16:26:11

敏感数据

2020-07-24 20:45:51

Spark数据集函数

2022-12-07 11:24:51

首席信息官IT

2009-07-03 17:48:24

JSP页面跳转

2021-06-23 13:57:13

数据泄露网络攻击数据安全

2011-04-21 10:08:34

2022-01-10 06:52:59

查询MySQL字段

2019-07-31 08:44:27

Session共享Memcache

2024-03-11 08:47:30

CRDT数据类型协同编辑

2022-11-23 13:46:02

云支出云计算

2020-04-02 10:45:48

多云云计算云平台

2015-09-10 09:30:54

Java多线程同步

2020-03-10 07:51:35

面试讽刺标准

2009-11-07 19:09:35

Windows 7优惠

2021-07-26 14:34:02

springboot 时间格式化项目

2023-07-21 08:00:00

API数字世界
点赞
收藏

51CTO技术栈公众号