今天做到关羽位运算的算法题的时候发现自己好像是一点也不是很了解。于是就开始找了有关位运算的题目开始补。
第一题:
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示
首先要了解以下的基本的位操作符:
符号 | 含义描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1 |
1 | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为0,相异为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
(或运算实际是“|”。markdowm不熟练不知道怎么打。记录下,下次会了在回来改正hhh)
分析:
这是牛客上的一道题目,假设
5 = 0000 0101
我们可以循环做移位运算5<<1后,与1做与运算,若等于1则count++。这样做确实是可以暂时得到我们想要的答案。
public static int NumberOf1_CanNotUse(int n) { int count = 0; while (n != 0) { if ((n & 1) == 1) { count++; } n = n >> 1; } return count; }
但是我们我们的无符号整数时包括负数的,负数用补码表示,假设n=-5时,其补码为-5=1111 1011,当-5>>1时,补码向右移的时候高位加1,这会导致我们的count++至+∞导致死循环。
所以我们应该换一下时目标数字往左移。即-5<<1,这样就会很好的解决了我们的问题,因为不管是正数还是负数<<1的时候都是在末位加0.
private static int NumberOf1_low(int n) { int count = 0; int flag = 1; while (flag != 0) { if ((n & flag) != 0) { count++; } flag = flag << 1; } return count; }
最优解:
public static int NumberOf1(int n) { int count = 0; while (n != 0) { ++count; //通过执行 n&(n-1) 操作来消除 n 末尾的 1 ,消除了多少次,就说明有多少个 1 。 n = (n - 1) & n; } return count; }
第二题:
给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
##分析:
观察二次幂:
2=1
4=10
8=100
16=1000
32=10000
...
可以很容易的看出来二次幂的规律,只有在其高位有1,其余都是为0。所以我们只需要将数字向右移即可
public boolean NumberOf1(int n) { int cnt = 0; while (n > 0) { cnt += (n & 1); n >>= 1; } return cnt == 1; }
但是后来看到了更加简单的例子,emmm大佬还是很多的啊!我等渣渣还是得好好努力。
public boolean NumberOf1(int n) { return (n>0) && ((n&(n-1)) == 0); }
第三题:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
##分析:
将所有元素做异或运算,即a[1] ^ a[2] ^ a[3] ^ …^ a[n],所得的结果就是那个只出现一次的数字
public int NumberOf1(int[] m) { int result = 0; for (int i : m) { result ^= i; } return result; }