题目一:找寻丢失的数字
题目描述:给定一个包含 [0, n]
中 n
个数的数组 nums
,找出 [0, n]
这个范围内没有出现在数组中的那个数。
解法思路:先将0-nums.length中的数都异或一遍得到一个结果,再拿这个结果去nums中异或,因为这个两个循环中,没有丢失的数字都是出现了两次的,而丢失的那个数只出现了一次,这样相同为0 不同为1的异或运算到最后只剩下那个丢失的数字
代码实现:
public int missingNumber(int[] nums) {
//得到数组的长度
int n = nums.length;
//记录结果
int result = 0;
//先将数组长度范围内的每个数进行异或运算,得到一个结果
for(int i = 0;i <= n;i++){
result ^= i;
}
//拿result的结果跟nums中的每一个数进行异或运算,异或出的结果就是nums中缺少,而在n范围内多于的那个数
for(int i = 0;i < nums.length;i++){
result ^= nums[i];
}
return result;
}
题目二:只出现一次的数字
题目描述:给你一个非空整数数组 nums
,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
思路:同样的异或运算,和上一道题类似,当每个数都是成对出现的,而多于的那个单独出现的数,一进行异或运算,就可以查找出来了
代码实现:
public int singleNumber(int[] nums){
if(nums.length < 2){
return nums[0];
}
//相同为0 不同为1 0异或任何数都等于这个数
int result = nums[0];
for(int i = 1;i <nums.length;i++){
result = nums[i] ^ result;
}
return result;
}
题目三:颠倒二进制位
题目描述:颠倒给定的 32 位无符号整数的二进制位n
思路:根据题目,给定的是32位的二进制数,所以只需要j将n循环32次,每次得到二进制数n的最后一位,然后用一个32位的变量,每次向左移动一位,腾出位置,来加上每一次得到的最后一位数,n再右移,去除最后一位数。最后的结果就是颠倒过来的二进制位
代码实现:代码给出了原始版和优化版本
//原始版
public int reverseBits(int n){
int result = 0;
//题目给定是32位 所以只需要循环32次
for(int i = 0;i < 32;i++){
//每一次得到n的最后一位数 (只需要&1即可) 与运算:两个数都是1才会返回1 否则返回0
int lastBit = n & 1;
//将result左移一位腾出位置并将lastBit 添加到result上面
result = (result << 1) + lastBit;
//将n右移一位 继续获得最后一位二进制数
n = n >> 1;
}
return result;
}
//优化:在result加值的过程中 可以直接采用或的形式 因为得到的n的最后一位二进制数要么是1要么是0 和result的值进行或运算,是不会对result前面的数造成影响的
public int reverseBits(int n) {
//位运算
int result = 0;
//给定的是32为数 所以循环32次即可
for(int i = 0;i < 32;i++){
//lastBit = (n & 1)
//result左移一位 腾出位置 放入lastBit
result <<= 1;
//result直接 | lastBit=(n & 1) 反正result最开始每一位都是0 所以或的结果最终就是lastBit
result |= (n & 1);
//将原二进制数右移一位 继续获得当前最后一位的二进制数
//>>>为无符号右移 无论最高位是什么 都填0
n >>>= 1;
}
return result;
}
题目四:位1的个数
题目描述:编写一个函数,输入是一个无符号整数(以二进制串的形式)n,返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。
思路:位运算,循环二进制数n,如果n不为0 ,就将其减去1,并更新n的值(采用与运算的方式)。
代码实现:
public int hammingWeight(int n) {
int result = 0;
//有几个1循环几次
while(n != 0){
n = n & (n-1);
result ++;
}
return result;
}
小细节补充:n & (n-1)操作的个人理解(查阅过资料)
n-1操作: 如果最低位为1 则直接减去 如果最低位为0 则向前借一位,如果前面也是0 则一直往前借 直到借到值然后拿过来运算。最低位减去1后为0,被借走的那一位也要相应的减去1。(相当于每次将n的最后一位1减去)
n&(n - 1)操作: 就是用旧的n来和新减去1的n进行与操作(两个都是1结果才为1否则为0),得到一个新的n。且既然进了循环证明肯定是有1的,所以result就加一,然后继续进行n&(n-1)操作。(相当于更新n的值)
题目四:比特位计数
题目描述:给你一个整数 n
,对于 0 <= i <= n
中的每个 i
,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1
的数组 ans
作为答案。
思路:使用汉明重量思想,对每一个数进行取最后一个1,累加数量
代码实现:第一个是旧版本,第二个是优化过后的
//旧版本
public int[] countBits(int n) {
int[] result = new int[n+1];
int count = 0;
//使用汉明重量的思想,将每一个i进行循环计算位1的个数
for(int i = 1;i <= n;i++){
int tmp = i;
while(tmp != 0){
tmp = (tmp & (tmp-1));
count ++;
}
result[i] = count;
count = 0;
}
return result;
}
/*
优化:一次循环
*/
public int[] countBits(int n){
int[] result = new int[n+1];
for(int i = 1;i <= n;i++){
result[i] = result[i&(i-1)] + 1;
}
return result;
}
优化版本的解释:
因为0~n范围内是层次递增的,每一个i去掉一个1之后 他的值都是i-1 ,可以每次计算i去掉一个二进制1之后的结果再加上1的结果,赋值给当前i,就是当前i中1的个数了
问题1:为什么还要加上一?
因为要通过当前数的二进制减1之后,得到一个对应的索引,该索引位置上的值加上1就是当前数的1的个数
如:4的二进制数中只有一个1 所在其减去一个1之后的二进制值结果是为0的,但是0位置上是没有1的,所以需要加1
5的二进制数中有两个1 ,所以在其减去一个1之后的二进制结果为0100 对应的就是十进制的4,而result[4]位置上的值是1,所以需要加一
总结:即:当前数的二进制减去一个1之后,在数组中对应位置上的值再加上1,就是该数的二进制1的个数,可以通过找规律发现。