位运算及其应用
1. 原码、反码、补码
定义:
原码、反码、补码是计算机存储一个数值的编码方式,只有有符号数才有原码,反码和补码
c/c++, (signed)char, (signed) short, (signed) int, (signed ) long long 都为有符号类型,而(unsigned)char, (unsigned)short,
(unsigned) int, (unsigned) long long为无符号类型,无符号类型相比有符号类型,有了大约一倍的正数取值范围,但却无法表示负数
& 按位与 : 全1才为1
| 或 有1则为1
^ 异或 相同则为1,不同则为0
~ 取反 a = 8 , 1000 , ~a = 0111, ~a = 7
>>(右移) 除以2
<<(左移) 乘以2
1.机器码:
机器数带符号,一般把二进制为的最高位定为其符号位,最高位为0是为非负数,为1是是负数。
例如2表示为00000000 00000000 00000000 00000010,
-3表示为10000000 00000000 00000000 00000011。
2.原码:原码就是符号位加上真值的绝对值
1表示为00000000 00000000 00000000 00000001。
-1表示为10000000 00000000 00000000 00000001。
3.反码:非负数和负数的表示方法有所不同
非负数:反码和原码一样
负数:将该负数的原码,符号位保持不变,其余各位进行取反运算
-3 : 10000000 00000000 00000000 00000011
~(-3) : 11111111 11111111 11111111 11111100
4.补码:非负数和负数的表示方法不同
非负数:补码和原码一样
负数:补码等于反码+1
-1
原码: 10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110
补码:11111111 11111111 11111111 11111111
在c/c++中,负数是用补码表示
n = 6 ,
6 原码(无符号类型):
原码:110
补码: 001
反码: 010
-n: 010
n & -n = 110 & 010 = 010
因此, n = 6的最右侧的1的位置为2
n = 10
原码: 1010
反码: 0101
补码: 0110
n & -n = 1010 & 0110 = 0010
2. 异或运算
a^a = 0; a^0 = 1; 0^0 = 0; 1^0 = 1; 1^1 = 0;
位运算满足交换律和结合律
a^b^c = a^c^b = b^c^a
1,2,2,4,4
1^2^2^4^4 = 2^2^4^4^1 = (2^2)^(4^4)^1 = 0^0^1 = 1;
3.位运算的优先级
四则运算(+-*\) > 移位 > 非等值比较(>,>=,<,<=) > 等值比较 (== !=) > 与或(& ,| ) > 赋值(=)
2.力扣题目
1.力扣191. 位1的个数
思路: lowbit(x)= x & -x; 求解出x最右侧的1, 不断拿x减去lowbit(x), 直到x为0;
例如:
n = 10
原码: 1010
反码: 0101
补码: 0110
n & -n = 1010 & 0110 = 0010 , 最右侧的1为0010, 2
#include<iostream>
using namespace std;
auto lowbit1(int n) -> int
{
return n & -n;
}
int lowbit2(int x) {
return x & -x;
}
int hammingWeight(uint32_t n) {
int cnt = 0;
while (n != 0) {
int c = n & -n;
n -= c;
cnt++;
}
return cnt;
}
int main() {
int a = 2;
cout << (a>>1) << " "<< (a<<1) << " " << ~a << endl;
int b = 10;
cout << "==lowbit==" << endl;
cout << lowbit2(b) << endl;
int c = -b;
while (c != 0) {
int a = c%2;
c = c/2 ;
cout << a << " ";
}
cout << endl;
cout <<"===" << endl;
unsigned int aa = -1;
cout << aa << endl; //4294967295
int d = 3, e = -3;
//~n = -(n+1)
cout << d <<" " << (~d) << " " << e << " " << (~e) << endl; //3 -4 -3 2
return 0;
}
2.力扣136 只出现一次的数字
思路1:哈希map
两次遍历
第一次++mp[nums[i]];
第二次 判断mp[nums[i]] == 1 ? return nums[i] : i++;
思路2:位运算
a^a = 0; a^0 = 1; 0^0 = 0; 1^0 = 1; 1^1 = 0;
位运算满足交换律和结合律
a^b^c = a^c^b = b^c^a
1,2,2,4,4
1^2^2^4^4 = 2^2^4^4^1 = (2^2)^(4^4)^1 = 0^0^1 = 1;
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
int singleNumber(vector<int>& nums) {
for (int i = 1;i<nums.size(); i++) {
nums[0] ^= nums[i];
}
return nums[0];
}
};
int main () {
cout << (0^0) << endl;
Solution solu;
vector<int> nums = {4,2,2,3,4};
cout << solu.singleNumber(nums) << endl;
int a = 1;
while (a < 16) {
//移位运算 数字 << 移动的位数, 2 << 1 , 010 -> 100, 右移同理
a = a<<1;
cout << a << endl;
}
return 0;
}
3. 力扣231:2的幂
思路1.:如果一个数是2的幂,那么 n & (-n) == n, 对于一个非负整数n, 其原码等于补码, 例如:8 & (-8) = 8;‘
n = 8
原码: 1000
反码: 0111
补码(反码+1):1000
思路2:
如果一个数是2的幂, 有 n&(n-1) = 0
8:1000
7: 0111
8&(8-1) = 0
4:100
3:011
4&(4-1)= 0
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
//超时, 暴力移位 每次*2
bool isPowerOfTwo(int n) {
if (n == 0) {
return false;
}
unsigned int a = 1;
while (a < n) {
a = a<<1;
if (a == n) {
return true;
}
}
return n>1 ? false : true;
}
//lowbit n&(-n)
// 8 原码 1000, 反码 0111, -8补码 = 反码+1 = 0111+0001 = 1000
bool isPowerOfTwo2(int n) {
//注意==运算符的优先级高于 &, 因此需要加括号
return n > 0 ? (n & -n) == n : false;
//另一种方法
//return n > 0 ? (n & n-1) == 0 : false;
}
};
int main() {
int n = -16;
Solution solu;
cout << solu.isPowerOfTwo2(n) << endl;
return 0;
}
4.力扣338:比特位计数
问题:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
思路:
枚举几个例子
0 1 2 3 4 5 6 7的二进制中1的个数分别为
0 1 1 2 1 2 2 3
当 nums[i]为偶数 即 nums[i] & 1 == 0
dp[i] = dp[i>>1] , i>>1 表示 i/2
当 nums[i]为奇数时,即 nums[i] & 1 == 1
dp[i] = dp[i>>1] + 1;
#include<iostream>
#include<vector>
using namespace std;
vector<int> countBits(int n) {
vector<int> dp(n+1,0);
dp[0] = 0;
for (int i = 1; i <= n; i++) {
// dp[i] = i%2 ? dp[i>>1] + 1 : dp[i>>1];
dp[i] = dp[i>>1] + (i&1);
}
return dp;
}
vector<int> countBits2(int n) {
vector<int> dp(n+1,0);
dp[0] = 0;
int highbit = 0;
for (int i = 1; i <= n; i++) {
if ((i & (i-1)) == 0) {
highbit = i;
}
dp[i] = dp[i-highbit] + 1; //+1 就是小于i的最高位的1
}
return dp;
}
int reverseBits(int num) {
int res = 0;
for (int i = 31; i >= 0; i--) {
res += (num & 1) << i;
num >>= 1;
}
return res;
}
int main() {
int n = 7;
vector<int> ans = countBits2(n);
for (int &x : ans) {
cout << x << " ";
}
cout << endl;
const int inf = 0x3f3f3f3f;
cout << 0x3f3f3f3f << endl;
return 0;
}
//https://blog.csdn.net/weixin_46141936/article/details/120899953
利用异或运算做加密解密
#include<iostream>
#include<vector>
#include<string.h>
using namespace std;
int main() {
//加密解密程序
string s = "i m a chinese!";
char key = 'b';
string out = "";
//加密
for (int i = 0 ; i < s.size(); i++) {
out += s[i]^key;
}
cout << out << endl;
char *p = new char[out.size()];
for (int i = 0; i < out.size(); i++) {
*(p+i) = out[i];
}
FILE *fp = fopen("file.txt","w");
fwrite(p,out.size(),1,fp);
fclose(fp);
//解密
string dencrpt = "";
for (int i = 0; i < out.size(); i++) {
dencrpt += out[i]^key;
}
cout << dencrpt << endl;
return 0;
}
//https://blog.csdn.net/weixin_46141936/article/details/120899953