共有如下几种位运算
运算符 | 功能 | 用法 |
---|---|---|
~ | 位取反 | ~expr |
<< | 左移 | expr1<<expr2 |
>> | 右移 | expr1>>expr2 |
& | 位与 | expr1&expr2 |
^ | 位异或 | expr1^expr2 |
| | 位或 | expr1 |
如果运算对象是带符号的并且符号为负,那么位运算如何处理运算对象的“符号位”依赖于机器,并且,左移可能会改变符号位的值。
左移运算对象可能会使对象发生提升,将char的8位变成int的32位。
>>有符号对象时,可能在左侧插入符号位的副本,也可能插入值为0的二进制位。
示例一
网页黑名单系统、垃圾邮件过滤系统、爬虫的网址判断重复系统, 并且系统容忍一定程度的失误率, 但是对空间的要求比较严格。=》布隆过滤器。
布隆过滤器
一个布隆过滤器精确地代表一个集合,可以精确地(非准确,存在误判)判断一个元素是否在集合中,精确程度取决于用户的具体设计,但做到100%的精确是不可能的。
布隆过滤器的优势在于使用很少的空间可以做到精确率较高。
假设有一个长度为m的bit类型数组记为bitarray,其中每个为止只占一个bit,只有0白,1黑两种状态。假设一共有k个哈希函数,函数的输出
,并且k个哈希函数足够的优秀彼此之间完全独立,则每一个对象经过k个哈希函数算出的结果也是相互独立的,可能相同也可能不同,对算出的每一个结果,对m取余,取余的结果在bitarray相应的位置写为1图黑,接下来处理所有的对象。对已经图黑的位置,让其继续为黑即可,至此一个布隆过滤器就生成了。这个布隆过滤器代表之前所有对象组成的集合。
假设一个对象为a,想检查a是否在布隆过滤器中,只用将a通过k个哈希函数,然后对所有结果m取余,然后对比bitarray的位置,如果全部为黑1,则这个对象判断为在布隆过滤器中,只要有一个位置不为1,则不在布隆过滤器中。
假设bitarray大小为m,哈希函数个数k,样本数量为n,失误率为p。
通常题目有n,p值,计算m的公式为
通常将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,其中只有一个数出现了奇数次,其他的数都出现了偶数次,请打印这个数,要求时间复杂度为
,额外空间复杂度为
。
分析:
异或的利用。
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,其中有两个数出现奇数次,其余数出现偶数次,找到这两个奇数次的数。要求时间复杂度为
,额外空间复杂度为
。
分析:利用例题四的方法,找到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