1. 题目
- 编写一个函数,输入无符号整数,返回二进制表达式中的数字位数为’1’的个数,也称为汉明重量:https://leetcode-cn.com/problems/number-of-1-bits/
- 给定一个整数,编写一个函数栏判断它是否是2的幂次方:https://leetcode-cn.com/problems/power-of-two/
- 比特位计数:给定一个非负整数num,对于0<=i<=num范围中的每个数字i,计算其二进制数中的1的数目并将它们作为数组返回:https://leetcode-cn.com/problems/counting-bits/
2. 基本知识
2.1 位运算介绍
计算机是用二进制存储和计算的,直接使用二进制进行运算,那么效率会高很多。
10进制转换为二进制:10进制的数除以2取余,再用得到的商除以2再取余,一直循环到商为0,然后将余数按高位到低位排列起来就是二进制数。
6的二进制:110
2.2 位运算常用的操作
- & : 与,两个位都为1,结果才为1
- | : 或,两个位都为0,结果才为0
- ^ : 异或,两个位相同为0,相异为1
- ~ : 取反,0变1,1变0
- << : 左移,各二进制位全部左移若干位,低位补0
-
: 右移,各二进制位全部右移若干位,无符号数,高位补0;有符号数,各编译器处理方法不一,有的补符号位(算术右移),有的补0(逻辑右移)
2.3 位运算的应用
2.3.1 异或
x ^ 0 = x 和0做异或运算,x不变
x ^ 1s = ~x ,1s表示位数全为1的数,任何数和1进行异或运算都是取反
x ^ (~x) = 1s
x ^ x = 0 因为每个位数都一样,所以得0
两个数交换
常规写法:
int temp = x;
x = y;
y = temp;
使用异或:
x = x ^ y; // ①
y = x ^ y; // ②
x = x ^ y; // ③
拆解一下(异或支持交换律和结合律的):
- ①是初始条件
- ②拆解为:y = (x ^ y) ^ y = x^ (y ^ y) = x ^ 0 = x
- ③拆解为:x = (x ^ y) ^ x = (x ^ x) ^ y = 0 ^ y = y
2,3,2 与运算
x = x &(x - 1) //清零最低位的1
x & -x // 可以得到最低位的1(并不一定是最后一位)
判断奇偶
%2写法:
x % 2 //结果为1,则x是奇数,结果为0,则x是偶数
与运算写法:
x & 1 //结果为1,则x是奇数,结果为0,则x是偶数
与运算解法拆解一下,6&1=> 110&001,前面的位数不管是0还是1,和0做&运算都是0,则结果为最后一位和1做&运算,得到如上的结果。
3. 算法题解
3.1 编写一个函数,输入无符号整数,返回二进制表达式中的数字位数为’1’的个数,也称为汉明重量
解法1:判断最后一位奇偶,然后左移一位,直到为0
该解法时间复杂度为O(1)(常数次循环),空间复杂度为O(1)
private int hanmingWeight(int n) {
int sum = 0;
while (n != 0) {
if ((n & 1) == 1) {
sum++;
}
n <<= 1;
}
return sum;
}
解法2:通过x & (x-1)这个公式,可以把最后一位1置为0,不断循环直到n为0
该解法时间复杂度和空间复杂度都为O(1)
private int hanmingWeight(int n) {
int sum = 0;
while (n != 0) {
n &= (n - 1);
sum++;
}
return sum;
}
3.2 给定一个整数,编写一个函数栏判断它是否是2的幂次方
2的幂次方有一个特点,换成二进制时,所有的位数值只有一个是1,其他为0,而x & (x-1)这个公式可以将最后一个1清零,则有以下的解法。
private boolean isPowerOfTwo(int n) {
return n > 0 & (n & (n - 1)) == 0;
}
3.3 比特位计数
给定一个非负整数num,对于0<=i<=num范围中的每个数字i,计算其二进制数中的1的数目并将它们作为数组返回
解法1 :循环每个数,找到每个数中1的个数
使用了汉明重量的算法
private int[] countBits(int num) {
int[] result = new int[num];
for (int i = 0; i < num; i++) {
int resultI = hanmingWeight(i);
result[i] = resultI;
}
return result;
}
解法2:
还是用到这个公式:x & (x-1),这个公式可以将最后一个1清零,那么x = x & (x-1) + 1,延伸出来就是 第i个数的1的个数为:count【i】 = count【i & (i - 1)】 + 1(因为i&*(i-1)只去掉最后一个1)
private int[] countBis(int num) {
int[] result = new int[num];
result[0] = 0;
for (int i = 1; i < num; i++) {
result[i] = result[i & (i - 1)] + 1;
}
return result;
}