位运算及其应用

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值