二进制的运算在算法题里面也有非常多的运用,目前我遇到的类型,一个是对二进制本身进行位运算,一个是利用二进制的位运算来使幂运算得到简化。二进制的运算在解决很多问题的时非常好用,因为在二进制运算中位运算其实可以对应到我们常见的乘除法中,但是由于二进制中1、0的含义可以标识是、否这两个状态,就使得二进制运算可以辅助判断、比较异同的操作,如果我们在做题时可以非常清晰地分辨二进制的标识含义和二进制的运算规律,那么许多问题都可以通过二进制的运算得到辅助的解决方式。
另外,二进制的移位运算虽然可以等价到乘除,但是其实在计算机的处理过程中,移位运算的开销要比乘除运算的开销小得多,所以平时在遇到求幂、乘除法的时候,如果涉及到二进制,建议往这个方向想一下,看看能不能利用二进制的移位运算降低操作开销。下面是我遇到的两个问题,都非常经典。
题目1:输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
说实话,在做这道题之前,我不太敢直接将一个整型数据直接进行二进制操作,的初始思路是转化为二进制字符串,然后转化为列表,然后再进行二进制的位运算……只能说自己掌握的知识还差火候,要不是做题我可能会一直局限在数据的形式上,虽然一直知道C++中整型多少位,char多少位,但是从来没有想过对一个显示位10进制的整型进行二进制的位操作其实完全是可以的。
对这个题,最好的方式是利用二进制的运算来求取1的个数,拿到一个数n,对其每一位进行检验,于是我们会有 bool flag, n&flag的基本思路,然后对这个向左移位 flag<<1,依次检验,循环32次得到结果。这种思路比我刚开始的思路(把一个可以直接处理的数转化为二进制字符串然后再折腾)要强,但是还可以跟进一步。
我们对一个二进制数110100进行减去1操作,发现其变为110011(根据加减进位非常好理解),然后我们再把它和原来的值与(可以把与:留同去异),110100&110011=110000,发现最右边的1消失了,那么我们就有了一个基本的思路,去掉一个数中所有的1,那么它有几个1,直接看我们消了几次不就完事了吗?所以我们对程序结构的规划就出来了,起码知道了循环操作中的基本操作就是:利用二进制表示消去它最右边的1。代码如下:
# -*- coding:utf-8 -*-
class Solution:#二进制运算考察
def NumberOf1(self, n):
# write code here
count=0
if n<0:
n=n&0xffffffff#python为无限精度,其他语言如C++这里可能不会用这条正负判断的分支语句
while n:
count+=1
n=((n-1) & n )#这个操作相当于将N的二进制数最右边的1化为0,使得对1的计数不用循环32次
return count
然后遇到的一个问题就是幂的快速计算,利用了数学中幂运算的拆解性质,比如5^7=5^(2^0+2^1+2^2)=5^(0111),为什么要写成这种形式,我们发现5的七次方其实就是5的1次方*5的2次方*5的4次方,利用二进制将指数拆解,然后我们在计算时发现在基底的基础上乘就可以了,比如这个问题,就是先求出5^1,然后求5^1*5^1,也就是5^2,然后求5^4,也就是5^2*5^2
题目如下:
题目2:
# -*- coding:utf-8 -*- class Solution: def Power(self, base, exponent): # write code here result=1 if base==0: return 0 if exponent==0: return 1 exp=abs(exponent) while exp: if exp&1:#比如01001010来&1,那么最后就可以指示result在2^n次到底要不要乘对应幂的数base^(2^n)。 result*=base base*=base exp>>=1 return [result,1/result][exponent<0]#这是if else写法的一个简化版