两个经典面试题(二进制思想)
1.你让工人为你工作7天,回报是一根金条,这个金条平分成相连的7段,每工作1天的回报就是1段。每天结束的时候,工人都有可能会向你要金条。如果只允许你两次把金条弄断,你如何给你的工人付费?
2.有1000个苹果,将它们放在10个箱子里,怎么放才能让我向你要苹果的时候,你都能整箱整箱的给我,你的给法是否唯一?
解答:
1. 工人可能在第1,2,3,4,5,6,7天结束时向你要金条,你给他的金条之和应分别为1,2,3,4,5,6,7.这样,如果把金条弄断两次,使得三段长分别为1,2,4,那每天结束时,如果这天工人找你要金条,你都可以满足这天工人可以得到的金条数是他工作的天数。因为1-7之间的数可以用3位二进制来表示,即1,2,4,而这恰好是金条的三段长。
比如:
工人第一天要金条:直接给段长为1
工人第一天要金条,第二天也要金条:给段长为2,同时收回段长为1
工人第一,二天未要金条,第三天要金条:给段长为1,2
工人第1天要金条,第2,3,4,5未要金条,第6天要金条:给段长2,4,收回段长为1
2. 要苹果的数量肯定是1-1000之间,也就是说任意给一个数1-1000之间,我必须知道它等于哪几个数之和。也是利用二进制思想,2^9 = 512, 2^10 = 1024,所以放1000个苹果需要10个箱子,每个箱子苹果数依次为
第0个箱子 2^0 = 1
第1个箱子 2^1 = 2
第2个箱子 2^2 = 4
第3个箱子 8
第4个箱子 16
第5个箱子 32
第6个箱子 64
第7个箱子 128
第8个箱子 256
第9个箱子 489 ( 1000 - (1 + 2 + … + 256) = 1000 - 511 - 489 )
由于1-1000任意一个数的二进制表示形式是唯一的,将其表示形式中为1的位数所对应的箱号拿出即可。
======================================================================================
原码
二进制中第一个数表示是正数还是负数(如matrix67所说) ,剩下的数表示所要表示的数的绝对值.
比如,0010=2, 1010=-2
反码
因为原码在计算机中不方便,引入了反码,反码就是负数在二进制中除了第一个数表示符号外,其他的数都
依次取反,比如127=01111111,而-127=10000000;
补码
虽然反码方便但是在计算中会错(正数和负数的计算),因为0000=0,1111=-0=0;有两个数表示 同一个数,所以在计算中会少一,补码就是在负数的反码上加上一。
比如-5=11111010(反码),补码为-5=11111011。
=================================================
各种位运算的使用
=== 1. and运算 ===
and运算通常用于二进制取位操作,例如一个数 and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数.
=== 2. or运算 ===
or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。
=== 3. xor运算 ===
xor运算通常用于对二进制的特定一位进行取反操作,因为异或可以这样定义:0和1异或0都不变,异或1则取反。
xor运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a xor b) xor b = a。xor运算可以用于简单的加密,比如我想对我MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 20665500,我就把20665500告诉MM。MM再次计算20665500 xor 19880516的值,得到1314520,于是她就明白了我的企图。
4. not运算 ===
not运算的定义是把内存中的0和1全部取反。使用not运算时要格外小心,你需要注意整数类型有没有符号。如果not的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用$0000到$FFFF依次表示的。下面的两个程序(仅语言不同)均返回65435。
如果not的对象是有符号的整数,情况就不一样了,稍后我们会在“整数类型的储存”小节中提到。
=== 5. shl运算 ===
a shl b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2 = 400。可以看出,a shl b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。
通常认为a shl 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。
定义一些常量可能会用到shl运算。你可以方便地用1 shl 16 – 1来表示65535。很多算法和数据结构要求数据规模必须是2的幂,此时可以用shl来定义Max_N等常量。
=== 6. shr运算 ===
和shl相似,a shr b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们也经常用shr 1来代替div 2,比如二分查找、堆的插入操作等等。想办法用shr代替除法运算可以使程序效率大大提高。最大公约数的二进制算法用除以2操作来代替慢得出奇的mod运算,效率可以提高60%。
常见的位运算
去掉最后一位 | (101101->10110) | x shr 1
在最后加一个0 | (101101->1011010) | x shl 1
在最后加一个1 | (101101->1011011) | x shl 1 or 1
把最后一位变成1 | (101100->101101) | x or 1
把最后一位变成0 | (101101->101100) | x and -2
最后一位取反 | (101101->101100) | x xor 1
把右数第k位变成1 | (101001->101101,k=3) | x or (1 shl (k-1))
把右数第k位变成0 | (101101->101001,k=3) | x and not (1 shl (k-1))
右数第k位取反 | (101001->101101,k=3) | x xor (1 shl (k-1))
取末三位 | (1101101->101) | x and 7
取末k位 | (1101101->1101,k=5) | x and (1 shl k-1)
取右数第k位 | (1101101->1,k=4) | x shr (k-1) and 1
把末k位变成1 | (101001->101111,k=4) | x or (1 shl k-1)
末k位取反 | (101001->100110,k=4) | x xor (1 shl k-1)
把右边连续的1变成0 | (100101111->100100000) | x and (x+1)
把右起第一个0变成1 | (100101111->100111111) | x or (x+1)
把右边连续的0变成1 | (11011000->11011111) | x or (x-1)
取右边连续的1 | (100101111->1111) | (x xor (x+1)) shr 1
去掉右起第一个1的左边 | (100101000->1000) | x and -x