位操作的应用

位操作的定义

所谓的位操作,是指按二进制逐位进行逻辑运算。常见的位运算包括:取反、位与、位或、位异或以及左移、右移。

在 C/C++中,基本的位运算符总结如下,其中运算符优先级为从上到下递减,且<<和>>优先级相同,如下表所示:

位运算符概览
操作符 功能 用法
~ 取反 ~var
<< 左移运算(相当于乘法) var<<pos
>> 右移运算(相当于除法) var>>pos
& 位与 var1 & var2
^ 位异或 var1 ^ var2
| 位或 var1 | var2

需要注意的是,位运算符只能用在带符号或无符号的 char、 short、int 与 long 类型上。

在实际应用中,建议用 unsigned 整型操作数,以免带符号操作数因为不同机器导致的结果不同:无符号数左移/右移默认移入的新位是 0。对于符号数,当最高位是 1(代表负数)时,有的机器认为右移移入的新位是 1。此外,复杂的位运算建议都用括号强制计算顺序,而不是依赖于优先级,这样做可以增加可读性并避免错误。

位操作的应用

最基本的操作包括获取位(获取第i位的值)、设置位(设置第i位为1)和清除位(设置第i位为0)。

  • 获取位可以利用&1:&(0x1 << i);
  • 设置位可以利用 |1:| (0x1 << i);
  • 清除位可以利用 &0:&(~(0x1 << i))

例子1:说明如下的代码做什么事情:(n & (n-1)) == 0.

分析:我们不难归纳得到 n &= (n-1)相当于 clear 最低的一位1,我们可以用这个方法统计一个整数的二进制表示中有多少个 1。

那么( n & (n-1) ) == 0 意味着该整数的二进制表示中只有一个 1,即它是 2 的次方。事实上,如果不能立刻看出结果,不妨尝试从 1 开始列举 n 的值,看看有什么规律。根据观察到的结果再来倒推表达式的含义。

例子2:给定整数的一个数组,每个元素都出现了两次,只有一个元素例外。请编写一个函数找到那个单个的元素。

分析: 当遇到某些题目需要统计一个元素集中元素出现的次数,应该直觉反应使用哈希表, key 是元素, value 是出现的次数。
扫描整个数组建立哈希表,再次扫描表看哪个元素出现了一次。这样做的时间复杂度为 O(n+n)。

这种解法具有普适性,应该首先想到。但是对于这道特殊的题目,能不能做的更好?我们考虑两个数如果相等,二进制表示有什么特点?很明显,当然是二进制表示每位比特都相等。能否通过某种二进制操作把两个相同整数变成常数?答案是异或: 相同整数异或得 0。 

如何把这个性质利用到本题?如果我们异或所有得元素,则出现两次的数都相互抵消,最后留下的就是单独的那个。
复杂度分析: 扫描数组一次,复杂度为 O(n)。

参考解答:

int singleValue(vector<int> array) {
int value = 0;
for (int i = 0; i < array.size(); i++) {
    value ^= array[i];
}
return value;
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章