位操作的应用实例(1)入门篇

一、前言

位运算在我们实际开发中用得很少,主要原因还是它对于我们而言不好读、不好懂、也不好计算,如果不经常实践,很容易就生疏。但实际上,位运算是一种很好的运算思想,它的优点自然是计算快,代码更少。

位运算的定义

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

在 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;
}

 

位操作的高级应用请参看:位操作的应用实例(2)位掩码

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值