剑指 Offer 15. 二进制中1的个数
方法一: 进行移位操作
class Solution { public: int hammingWeight(uint32_t n) { int count = 0; for(int i = 0; i < 32; i++) { if(n>>i&1) count++; } return count; } };
方法二:对方法一进行优化,因为有的n不需要移位32位
class Solution { public: int hammingWeight(uint32_t n) { int count = 0; while(n) { count += (n&1); n>>=1; } return count; } };
方法三:不是n进行移位操作,而是1进行移位操作
class Solution { public: int hammingWeight(uint32_t n) { int res = 0; while(n) { if(n&0x1) res++; n = n>>1; } return res; } };
方法四:利用n&n-1 可以消除 n中最右边的1
class Solution { public: int hammingWeight(uint32_t n) { int count = 0; while(n) { n&=n-1; count++; } return count; } };
方法五:查表法
class Solution { public: int hammingWeight(uint32_t n) { int count = 0; int table[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; while(n) { count += table[n&0xf]; n>>=4; } return count; } };
方法6:二进制平行算法
class Solution { public: int hammingWeight(uint32_t n) { n = (n&0x55555555)+((n>>1)&0x55555555); //n为32位,相邻两位进行相加 n = (n&0x33333333)+((n>>2)&0x33333333); // 相当于2位为一个单位, 相邻两个单位相加 n = (n&0x0f0f0f0f)+((n>>4)&0x0f0f0f0f); // 相当于4位为一个单位, 相邻两个单位相加 n = (n&0xff00ff)+((n>>8)&0x00ff00ff); // 相当于8位为一个单位, 相邻两个单位相加 n = (n&0xffff)+((n>>16)&0xffff); //相当于16位为一个单位, 相邻两个单位相加 return n; } };
leetcode231. 判断一个数是否为2 的幂
思路:一个数 如果是2的幂,那么该数的二进制中质保函1个1.
方法一: 利用移位来计算n中1的个数
class Solution { int count1Num(int n) //计算n 若用二进制表示时 其中1的个数 { int count = 0; while(n) { count += (n&1); n>>=1; } return count; } public: bool isPowerOfTwo(int n) { return n > 0 && count1Num(n) == 1; } };
方法二:利用n&(n-1)可以去掉 n中最右边的1
这种方法超级简单
class Solution { public: bool isPowerOfTwo(int n) { return n > 0 && (n&(n-1))==0; } };
剑指 Offer 56 - II. 数组中数字出现的次数 II
其中一个数只出现一次,另外的数都出现三次,找到这个出现一次的数。
统计数组中,所有元素在 32位中每一位 1出现的次数,如果某一位上1出现的次数 count1Num%3 ==1,说明 该位上的1属于那个只出现一次的数。
class Solution { public: int singleNumber(vector<int>& nums) { int res = 0; for(int i = 0; i < 32; i++) { int count1Num = 0; for(int j = 0; j < nums.size(); j++) { count1Num += (nums[j] >> i)&1; } if(count1Num % 3 != 0) { res |= (1<<i); } } return res; } };
对于数组中,如果只有一个数字出现一次,其他数字都出现奇数次n(n > 1),我们可以用下面代码来找到只出现一次的数字。
class Solution { public: int singleNumber(vector<int>& nums) { int res = 0; for(int i = 0; i < 32; i++) { int count1Num = 0; for(int j = 0; j < nums.size(); j++) { count1Num += (nums[j] >> i)&1; } if(count1Num % n != 0) { res |= (1<<i); } } return res; } };
对于数组,如果只有一个数字出现一次,其他数字都出现偶数次,我们只需要把所有数字异或一
遍即可找到出现一次的数字。
对应leetcode136. 只出现一次的数字
class Solution { public: int singleNumber(vector<int>& nums) { int size = nums.size(); if(size == 0) return 0; int res = 0; for(int i = 0; i < size; i++) { res ^= nums[i]; } return res; } };
leetcode 1442. 形成两个异或相等数组的三元组数目
思路:让 a = b 即 a ^ b = 0 即:
我 们 只 需 要 从 数 组 arr 中 找 到 一 些 连续的 元 素 , 他 们 的 异 或 结 果 等
于0即可。
class Solution { public: int countTriplets(vector<int>& arr) { int count = 0; int size = arr.size(); for(int i = 0; i < size - 1; i++) { int temp = arr[i];//注意 起始值为arr[i] for(int k = i + 1; k < size; k++) { temp ^= arr[k]; if(temp == 0) { count += k - i; } } } return count; } };
剑指 Offer 53 - II. 0~n-1中缺失的数字
方法一:利用位运算 nums[i] &i
class Solution { public: int missingNumber(vector<int>& nums) { int size = nums.size(); int res = 0; for(int i = 0; i < size; i++) { res ^= nums[i]^i; } return res^size; } };
方法二:暴力遍历
class Solution { public: int missingNumber(vector<int>& nums) { for(int i = 0; i < nums.size(); i++) { if(nums[i]!=i) return i; } return nums.size(); } };
方法三:利用二分法
class Solution { public: int missingNumber(vector<int>& nums) { int left = 0, right = nums.size() - 1; while(left <= right) { int med = (left + right)/2; if(nums[med] == med) left = med + 1; else right = med - 1; } return left; } };
leetcode461:https://leetcode-cn.com/problems/hamming-distance/ 汉明距离
class Solution { public: int hammingDistance(int x, int y) { int temp = x^y;//在求temp对应的二进制中1的个数 int res = 0; while(temp) { if(temp & 1 == 1) res++; temp >>= 1; } return res; } };
剑指 Offer 56 - I. 数组中数字出现的次数
一个整型数组 nums
里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
class Solution { public: vector<int> singleNumbers(vector<int>& nums) { int size = nums.size(); int temp = 0; for(int i = 0; i < size; i++) { temp ^= nums[i]; } temp &= -temp; int res1 = 0, res2 = 0; for(int j = 0; j < size; j++) { if((temp & nums[j]) == 0)//(temp & nums[j])注意要加括号 { res1 ^= nums[j]; } else { res2 ^= nums[j]; } } return {res1, res2}; } };
方法一:
int lowPower(int n) { int temp = 1; while(n > temp) { temp <<= 1; } return temp; }
方法二:利用位运算
举例:
所 以 一 种 最 简 单 的 方 式 就 是 通 过 移 位 运 算 , 把 53 最 左 边 的 1 全 部 往 右 边 铺 开 , 就 变 成 了00111111,然后再加1就变成了了01000000。
但 是 这 里 有 个 小 问 题 就 是 , 如 果 x 本 来 就 是 2 的 n 次 方 , 比 如 x 是 16 , 运 算 的 结 果 就 会 变成 32 , 而我 们 实 际 要 求 的结果应该是16 。 所 以 这 里 我 们 可 以 先 让 x-1 , 然 后 再 进 行 运 算。
int lowPower(int n) { n = n - 1; //将最高位1之后的都变为1 n |= (n>>1); n |= (n>>2); n |= (n>>4); n |= (n>>8); n |= (n>>16); return n+1; }
a = 3 011
b = 5 101 a&b = 001 a&b<<1 表示有进位 a^b = 110 表示除了进位剩下的 位的数
(a&b<<1) + (a^b) = a + b = 8 又不能使用+ ,因此可以利用递归实现。
方法一:递归的写法存在问题:
如果是负数 leetcode左移时报错 runtime error: left shift of negative value
具体原因是运行时库的问题?
可以转换成无符号整型进行左移
注意:为了解决上述问题,要负数转化为unsigned int类型,因此 ((unsigned int)a&b)<<1
class Solution { public: int getSum(int a, int b) { if(a == 0 ||b == 0) return a^b; return getSum(a^b, ((unsigned int)a&b)<<1); } };
方法二:
class Solution { public: int getSum(int a, int b) { while(b) { unsigned int temp = ((unsigned int)(a&b))<<1; a ^= b; b = temp; } return a; } };
方法一:利用递归
//实现 a - b int substract(int a, int b) { if(b == 0) return a; int c = a&b; //找到a和b 相同位的1 a ^= c; //去除a中 与b相同的部分 b ^= c; //去除b中 与a相同的部分 substract(a|b, b<<1); }
方法二:
//实现 a - b int substract(int a, int b) { while(b) { int temp = a^b; a ^= temp; b ^= temp; a |= b; b <<= 1; } return a; }
leetcode 693. 交替位二进制数 https://leetcode-cn.com/problems/binary-number-with-alternating-bits/
注意:加上long 是为了避免当 n为2^31 -1时, n+1会越界
class Solution { public: bool hasAlternatingBits(int n) { n ^= (n>>1); return (n&((long)n+1)) == 0; } };