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

本文介绍了位运算的基础知识,特别是C++中的应用。讲解了布隆过滤器的概念和优势,用于在有限空间内判断元素是否存在,允许一定误差。并给出了六个位运算相关的面试题目,包括不使用额外变量交换整数、寻找数组中出现奇数次的数等,涉及位运算的巧妙运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

共有如下几种位运算

运算符功能用法
~位取反~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 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=(ln2)2n×lnp,
通常将m向上取整。哈希函数个数k的计算公式为
k = ln ⁡ 2 × m n , k = \ln 2\times \frac{m}{n}, k=ln2×nm,
计算最终的失误率公式为
p = ( 1 − e − n k m ) k . p = (1-e^{-\frac{nk}{m}})^k. p=(1emnk)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(N),额外空间复杂度为 O ( 1 ) 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(N),额外空间复杂度为 O ( 1 ) 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值