位运算基础与面试题C++

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37895339/article/details/79758306

共有如下几种位运算

运算符 功能 用法
~ 位取反 ~expr
<< 左移 expr1<<expr2
>> 右移 expr1>>expr2
& 位与 expr1&expr2
^ 位异或 expr1^expr2
| 位或 expr1

如果运算对象是带符号的并且符号为负,那么位运算如何处理运算对象的“符号位”依赖于机器,并且,左移可能会改变符号位的值。
左移运算对象可能会使对象发生提升,将char的8位变成int的32位。
>>有符号对象时,可能在左侧插入符号位的副本,也可能插入值为0的二进制位。

示例一

网页黑名单系统、垃圾邮件过滤系统、爬虫的网址判断重复系统, 并且系统容忍一定程度的失误率, 但是对空间的要求比较严格。=》布隆过滤器。

布隆过滤器

一个布隆过滤器精确地代表一个集合,可以精确地(非准确,存在误判)判断一个元素是否在集合中,精确程度取决于用户的具体设计,但做到100%的精确是不可能的。
布隆过滤器的优势在于使用很少的空间可以做到精确率较高。

假设有一个长度为m的bit类型数组记为bitarray,其中每个为止只占一个bit,只有0白,1黑两种状态。假设一共有k个哈希函数,函数的输出 m \ge m ,并且k个哈希函数足够的优秀彼此之间完全独立,则每一个对象经过k个哈希函数算出的结果也是相互独立的,可能相同也可能不同,对算出的每一个结果,对m取余,取余的结果在bitarray相应的位置写为1图黑,接下来处理所有的对象。对已经图黑的位置,让其继续为黑即可,至此一个布隆过滤器就生成了。这个布隆过滤器代表之前所有对象组成的集合。
假设一个对象为a,想检查a是否在布隆过滤器中,只用将a通过k个哈希函数,然后对所有结果m取余,然后对比bitarray的位置,如果全部为黑1,则这个对象判断为在布隆过滤器中,只要有一个位置不为1,则不在布隆过滤器中。

假设bitarray大小为m,哈希函数个数k,样本数量为n,失误率为p。
通常题目有n,p值,计算m的公式为
m = n × ln p ( ln 2 ) 2 , m = -\frac{n\times\ln p}{(\ln2)^2},
通常将m向上取整。哈希函数个数k的计算公式为
k = ln 2 × m n , k = \ln 2\times \frac{m}{n},
计算最终的失误率公式为
p = ( 1 e n k m ) k . p = (1-e^{-\frac{nk}{m}})^k.

设计过程:
1.根据样本数量n与失误率p计算过滤器大小m。
2.根据过滤器大小m以及样本数量n计算哈希函数个数k。
3.计算最终的失误率p。

例题二

如何不用任何的额外变量交换两个整数的值。
分析:

a = a^b;
b = a^b;
a = a^b;

上述可理解为

b = a0^b0^b0;
a = a0^b0^a0;

例题三

给定两个32位整数a和b,返回a和b中较大的,但是不能用任何比较判断。
分析:
思路一:得到a-b的符号位。
当a-b溢出时,可能会发生错误。

int sign(int n) {
	return (n >> 31) & 1;
}

int maxNumber(const int &a, const int &b) {
	int c = a - b;
	int signC = sign(c);
	return signC * b + (1 - signC)*a;
}

思路二:判断a,b是否异号

int sign(int n) {
	return (n >> 31) & 1;
}

int maxNumber(const int &a, const int &b) {
	int signA = sign(a);
	int signB = sign(b);
	int diff = signA ^ signB;
	int c = a - b;
	int signC = sign(c);
	return diff * ((1 - signA)*a + (1 - signB)*b) + (1 - diff)*(signC*b + (1 - signC)*a);
}

例题四

给定一个整形数组arr,其中只有一个数出现了奇数次,其他的数都出现了偶数次,请打印这个数,要求时间复杂度为 O ( N ) O(N) ,额外空间复杂度为 O ( 1 ) O(1)
分析:
异或的利用。
1.n与0异或得n
2.n与n异或得0
3.异或运算满足交换律
4.异或运算满足结合律

异或得顺序可以任意重排,不会改变原来的结果。
若arr为[A,B,C,A,B,C,D]
可等价于异或[A,A,B,B,C,C,D]
让一个变脸eo依次异或arr数组中的数,最终得到的结果为出现了奇数次的数。

int oddNumber(int *arr, const int &length) {
	int eo = 0;
	for (int i = 0; i < length; ++i) {
		eo ^= arr[i];
	}
	return eo;
}

例题五

给定一个数组arr,其中有两个数出现奇数次,其余数出现偶数次,找到这两个奇数次的数。要求时间复杂度为 O ( N ) O(N) ,额外空间复杂度为 O ( 1 ) O(1)
分析:利用例题四的方法,找到eo=a^b,在eo中找到为1的比特位,然后用这一位筛选arr数组中的数,再申请一个eo2去异或筛选出的数即为第一个奇数次的数,然后eo^eo2为第二个奇数次的数。

void twoOddNumber(int *arr, const int &length, int ans[2]) {
	int eo = 0;
	for (int i = 0; i < length; ++i) {
		eo ^= arr[i];
	}
	int index = findOneBit(eo);
	if (index < 0) {
		return;
	}
	else {
		int eo2 = 0;
		for (int i = 0; i < length; ++i) {
			if ((arr[i] >> index) & 1) {
				eo2 ^= arr[i];
			}
		}
		ans[0] = eo2;
		ans[1] = eo2 ^ eo;
	}
	
}

例题六

请设计一种加密过程,完成对明文text的加密和解密工作。
分析:异或运算可以完成简单的加密与解密过程。
明文text,用户给定密码pw,假设密文为cipher

cipher = text ^ pw
text = cipher ^ pw = (text ^ pw) ^ ps
	=text ^ (pw ^ pw)=text

猜你喜欢

转载自blog.csdn.net/weixin_37895339/article/details/79758306
今日推荐