拜托,面试别再让我数1了!!!

简介:

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

画外音:姊妹篇《拜托,面试别再问我TopK了!!!》。

例如:

uint32_t i=58585858;

i的二进制表示是:

0000 0011 0111 1101 1111 0011 0000 0010

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

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

一、位移法

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

伪代码

do{

    if ((n&1)==1){

       result++;

    }

    n>>= 1;

    i++;

} while(i<32);

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

二、求与法

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

最末位一个1会变成0
最末位一个1之后的0会全部变成1
其他位相同

栗子

           x = 1011 0000

         x-1= 1010 1111

x & (x-1) = 1010 0000

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

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

伪代码

while(n){

   result++;

   n&=(n-1);

}

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

画外音:校招时,我问过这样的面试题,“如何快速判断一个正整数是不是2的x次幂”,巧妙解法是

return !(n&(n-1));

即,如果n是2的x次幂,二进制表示只有一个1。

三、查表法

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

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

1的二进制表示中包含1个1

2的二进制表示中包含1个1

3的二进制表示中包含2个1

58585858的二进制表示中包含15个1

...

提前计算好结果数组:

result[1]=1;

result[2]=1;

result[3]=2;

result[58585858]=15;

伪代码

return result[n];

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

内存分析

假如被分析的整数是uint32,打表数组需要记录2^32个正整数的结果。

n的二进制表示最多包含32个1,存储结果的计数,使用5个bit即可。

故,共需要内存2^32 * 5bit = 2.5GB。

画外音:5个bit,能表示00000-11111这32个数。帮忙看下,算错了没有,上一篇文章bit和Byte算错了8倍。

四、二次查表法

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

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

思路

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

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

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

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

伪代码

uint16 n1 = n & 0xFFFF;

uint16 n2 = (n>>16) & 0xFFFF;

return  result[n1]+result[n2];

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

内存分析

被分析的整数变成uint16,打表数组需要记录2^16个正整数的结果。

n1和n2的二进制表示最多包含16个1,存储结果的计数,使用4个bit即可。

故,共需要内存2^16 * 4bit = 32KB。

画外音:帮忙看下,算错了没有。

好神奇!!!

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

五、总结

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

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

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

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

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

知其然,知其所以然

思路比结论重要。

希望大家对“数1”有新的认识,谢


原文发布时间为:2018-09-26

本文作者:58沈剑

本文来自云栖社区合作伙伴“架构师之路”,了解相关信息可以关注“架构师之路

相关文章
老程序员分享:leetcode笔记201.BitwiseANDofNumbersRange
老程序员分享:leetcode笔记201.BitwiseANDofNumbersRange
42 0
这篇写给想选计算机专业的学弟学妹们
另外,这次我专门在自己母校拍了个视频,也算做个小宣传。但因为没经验、没设备,所以拍得比较业余,有人表示根本看不下去图片。纠结了一番我决定还是发出来。我经常跟同学说,你开始写代码不知道怎么写太正常不过了,谁不都是从小白过来的。
“2023金三银四”又来了,关于面试,你需要知道的这些事
没有跳不了的槽,只有没用心的人,2023最新后端岗位面试真题+简历包装。
138 0
“2023金三银四”又来了,关于面试,你需要知道的这些事
真香!肝完Alibaba这份面试通关宝典,我成功拿下今年第15个Offer
前言 金九银十已到,不少人找LZ咨询,问我现在的面试需要提前准备什么?为了造福更多的开发者,也为了让更多的小伙伴通过面试;LZ近期也一直想着怎么才能帮到大家。所以近期在各大渠道整合大厂相关面试题,并结合了我一位现在已经入职阿里(阿里的Offer就是他今年的第15张offer)的朋友一整年的面试经历,为大家打造出一份金九银十Java面试通关宝典。
面试自我介绍
其实很多人也会和题主有一样的疑惑:明明简历上已经有自我介绍了,为什么面试的时候还要再说一遍?面试时的自我介绍,和简历上的又有怎样的区别呢?相比于简历上的,面试时的自我介绍应该更口语化,且是目的地去引导后面的问题的。
257 0
应届生该如何准备面试,我有话想说。
面试的前提准备包含5~6个方面:简历编写、项目经验、技术能力、简历投递(牛客网、脉脉、智联招聘、BOSS直聘)、面试准备、算法准备等等内容。 面试过程包含内容:一般分为2~3轮技术面试+1轮HR面试,1轮技术面试又包含了基础知识和业务逻辑面试和算法面试(中大厂必备)。 基础知识面试:对专业知识是否掌握,掌握的熟练度又是几成。比如学校学习的操作系统、计算机组成原理、计算机网络、数据结构、数据库甚是前端相关知识都可以考察。 业务逻辑面试:对简历上的项目考察,面试官根据项目的内容进行考察,比如项目某个功能为什么要这么做?优缺点分析,假如重新让你再做一次,你会怎么做。。。然后会针对你所做的东西进行深度
245 2
应届生该如何准备面试,我有话想说。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等