看题第一思路
- IO(input、output)分别为:I:整数(我用java语言实现,因此需要将n看作无符号整数);O:该数对应的二进制表示中1的个数。
- Q1:从题目中例子来看,该整数是二进制还是十进制输入呢?如何甄别呢…
- Q2:对于Java,有符号整数是如何表示的?
- 本题是位运算tag下的题,涉及的位运算一般涉及与(&)、或(|)、非(-)、异或(^)、移位(<<、>>)运算。如何应用在本题里?
- 思路1:可以将输入的整数转换为二进制,然后循环,逐个判断该二进制数的每一位上的数是否为1,是则count++。
思路1:
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
String str = Integer.toBinaryString(n);
int count=0;
char[] res = str.toCharArray();
for(int i=0;i<str.length();i++) {
if(res[i]=='1')
count++;
}
return count;
}
}
Integer类
的toBinaryString()
方法,将输入转换为二进制字符串;String类
的toCharArray()
方法,将字符串转换为字符数组;- 遍历字符数组的每个元素,与字符
1
比较,以得到1
的个数 - 可以看到这种方法时间效率低
题解思路
- 位运算:左移运算符
m<<n
表示将m左移n位,即将m最左边的n位丢弃,在最右边补n个0;
右移运算符m>>n
表示将m右移n位,即将m最右边的n位丢弃,在最左边补n个符号位。 - 若
(n&1)==1
,则n的最右边一位是1,否则是0。 n&(n-1)
会把n最右边的1变成0:若n
的二进制表示中,最右边的1
位于第m位;则n-1
时,第m位由1
变成0
,而第m位之后所有0
都变成1
,整数中第m位之前的所有位保持不变;n&(n-1)
时,相当于把它最右边的1
变成0
。
思路2: 利用(n&1)==1
- 可以将n逐次右移一位,和
1
做与(&)
运算;这样做的问题在于对于别的语言可能会造成死循环。 - 可以将
1
做左移运算,并和n
做与(&)
运算,以判断n的最低位、次低位…是不是1
。
public int hammingWeight(int n) {
int count=0;
while(n!=0) {
if((n&1)==1) count++;
n=n>>>1;
}
return count;
}
这里需要注意:java中右移运算符>>和无符号右移运算符>>>的区别;
java中右移运算符>>和>>>
- 时间复杂度:
l
o
g
2
n
log_2{n}
log2n(即n转换为二进制的位数)
思路3: 利用n&(n-1)
public int hammingWeight(int n) {
int count=0;
while(n!=0) {
count++;
n = (n-1) & n;
}
return count;
}
- 时间复杂度:count(即n转换为二进制后1的个数)
位运算
位运算为什么快:因为计算机内存中的数据是以0 1形式存储的,即二进制形式存储的,当进行二进制运算时无需将其转换成十进制,所以会快。
应用
与运算: 取一个数的特定位。
n&1
:判断奇偶,就是取二进制的最末位(如果n的最末位是1,表示n是奇数,如果n的最末位是0,表示n是偶数)。所以结果==1,则n的最末位是1,表示n是奇数,否则为偶数。
或运算: 通常用于二进制特定位上的无条件赋值。(例如可对一个数据的某些位设置为1)。
n|1
:结果就是把二进制最末位强行变成1,若要变成0,则再-1
。
异或运算: 定义:0和1异或0都不变,异或1则取反。即相同为0,不同为1)
- 通常用于对二进制的特定一位进行取反操作。
- 实现交换两个数
a ^= b;
b ^= a;
a ^= b;
左移运算: a<<b代表a乘以2的b次方。
右移运算: a>>>b代表a除以2的次方b运算,通常应用于二分查找、堆的插入。
(注意算术右移和逻辑右移的区别)