题目来源,待字闺中,原创@陈利人 ,欢迎大家继续关注微信公众账号“待字闺中”
一个整数,可以表示为二进制的形式,请给出尽可能多的方法对二进制进行逆序操作。 例如:10000110 11011000的逆序为 00011011 01100001
分析:题目中说是一个整数,对它的二进制进行逆序。并不是一个01字符串,或者01的数组。那么我们该如何解决这个问题呢?方法还是比较多的,有的中规中矩、有的非常巧妙。我们要掌握中规中规的方法,见识更多的巧妙的方法。慢慢的,能够举一反三,在遇到新的问题时,能够有灵思妙想。
最直接的方法
int BinaryReverse(int v) { unsigned int t = (unsigned int)v;//防止右移时左边补1 int res = 0,s = 32; for(;0 != v;v >>= 1) { res <<= 1; res |= (v & 0x01); s --; } res <<= s; return res; }
代码比较好理解,取到v的最低位,作为res的最高位;v每取一次最低位,则右移一位;res每确定一位,则左移一位。同时记录移动了多少位,最终要补齐。
通过查表的方法
在遇到位操作的问题时,往往题目中限定了总的位数,比如这个题目,我们可以认为32位。这就给我们带来了一个以空间换时间的解决思路:查表法。位数是固定的,可以申请空间,存储预先计算好的结果,在计算其他的结果的时候,则查表即可。
32位相对于查表来讲,还是太大了。既然这样缩小范围,32个bit,也就是4个byte。每个byte 8bit,可以表示0-255的整数。可以通过申请256大小的数组,保存这256个整数,二进制逆序之后的整数。然后将一个32位的整数,划分为4个byte,每一个byte查表得到逆序的整数:r1,r2,r3,r4。按照r4r3r2r1顺序拼接二进制得到的结果就是最终的答案。
这是一个思路,大家可以进一步思考,尝试。
巧妙的方法
我们这里主要分析这个巧妙的方法,核心思想是:分治法。即:
最后一个2位的逆序,直接交换即可。也就是分治递归的终止条件。但是,在上面的过程中,还没有应用到位操作的技巧。根据动态规划的思想,我们可以自底向上的解决这个问题:
2组16位的交换,完成32位的逆序
通过下面的例子,详解上面的过程,我们以16位为例:10000110 11011000
1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 |
0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 |
0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
经过4步,逆序完成。推而广之,总的时间复杂度为O(logn),n是二进制的位数。这个方法可以推广到任意位。
int BinaryReverse(int v)//下面按位与的4种策略不确定,可以有不同的方法,但是结果是一样的 { unsigned int res = (unsigned int)v;//防止右移时左边补1 res = ((res & 0xAAAAAAAA) >> 1) | ((res << 1) & 0xAAAAAAAA);//(res & 0xAAAAAAAA) >> 1)表示取偶数位然后右移,(res << 1) & 0xAAAAAAAA)表示把奇数位左移 res = ((res & 0xCCCCCCCC) >> 2) | ((res << 2) & 0xCCCCCCCC);//把偶数两位右移,奇数两位左移 res = ((res & 0xF0F0F0F0) >> 4) | ((res << 4) & 0xF0F0F0F0); res = ((res & 0xFF00FF00) >> 8) | ((res << 8) & 0xFF00FF00); res = (res >> 16) | (res << 16); return res; }
原文地址:http://blog.csdn.net/fangjian1204/article/details/38085757