c语言位运算知乎,c语言或者cpp中位运算的技巧

简述

在知乎上看到一个题目,解答很有意思,用的是位运算。

这让我觉得位运算有更多的算法可能,但是却还没被我用过。

这种东西都是第一次看,觉得挺牛的,第二次,第三次看的时候就觉得没什么了。So,大佬们轻喷。

基础

涉及到二进制运算,如下:

& | ^ 三种运算,分别对应着交,并,异或

交:两个对应位都是1,则为1,否则为0

并:两个对应位只有一个是1,则为1,否则为0

异或:两个对应位相同则为0,不同则为1

>> 往右移动,箭头相反则为往左移动

这就是所有二进制的运算了。但就是由这里的这些推导出了很多有趣的运算范式。这些范式也可以称之为基础。

A为任何数,

0 & A = 0

0 | A = 0

0 ^ A = A

A ^ A = 0

第三条需要解释下:二进制只有0或者1。0的二进制任何位置都是0。A的数,任何一个位置。如果是1,则1和0不同,该位置还是1。如果该位置为0,则0和0相同,该位置还是0。故不变

明显,三则运算满足交换律和结合律。

这个验证简单,画个表就知道了。只需要考虑任取一个位置,画对应的表就好了。

交换:A,B两个数交换

A = A ^ B

B = A ^ B

A = A ^ B

就完成交换了。

这种交换方式也是非常基础的acm东西。如果解释也是非常简单的。(注意,下面的表达式中, 每个表达式的第一个均为赋值的意思,后面的等号就是相等的意思)

第一步:A = A ^ B

第二步实际上为:B = (A ^ B) ^ B,再由交换律和结合律,变为 B = A ^ (B ^ B) = A ^ 0 = 0 ^ A = A

第三步实际上为:A = (A ^ B) ^ A, (原来这里的B已经是实际上的A了),同样的, A = B。

扩展应用

数字去重

这个就是我之前说的,看到了那篇文章:https://www.zhihu.com/question/33776070/answer/703697146?utm_source=wechat_session&utm_medium=social&utm_oi=806197504654839808

这里说的去重,是

把重复两次(或者是偶数次)的那些数字给去掉。

而且数据集中只能有一个只出现一次的数字。

(应该是个相当naive的问题了)

这种问题,由于约束比default的版本更多,所以说,才可以用上这个答主给的那个版本的代码。

例如:

数据集为 1 2 3 4 5 1 2 3 4, 那么我们要找到这个5

其他实例为: 1 2 3 1 2 要找到 3。这里只会有一个目标数字,且每个数字的重复都只有一次。

1 2 1 2 1 2 3 这样的数据就不行了。

1 2 3 1 2 4 这样的数据集,就需要改进了。这个改进回答也有,但我嫌弃他解释的太麻烦。

方法: 异或

1^2^3^4^5^1^2^3^4 = (1^1)^(2^2)^(3^3)^(4^4)^5 = 0 ^ 5 = 5

0f05623ea58e2cc7f0c14811be496f65.png

#include

using namespace std;

int main() {

int a[] = { 1,2,3,4,5,1,2,3,4};

int tmp = a[0];

for (int i = 1; i < 9; ++i)

tmp ^= a[i];

cout << tmp << endl;

system("pause");

}

去重改进

如上所说,可以改进到

把重复两次(或者是偶数次)的那些数字给去掉。

而且数据集中只能有两个只出现一次的数字。(且只能有两个)

标准数据是:1 2 3 1 2 4

回忆下上一个:

其实我们这得到的就是第一步得到的 A ^ B

那想把A或者B提取出来。按照知乎那哥们那么说也是可以的。但是真的是麻烦

简单点: A ^ (A ^ B) = B。且异或运算也是唯一性的。给一个数组,设置为空。

遍历:拿到第i个数,是否已经在上面新建的数组中了,在则这个数就是我们想要的,否者,将 这个数和A^B做异或的数值存下来。

如下:

52bde8e63c7db541f3f6640372403917.png

注意到:这个时间复杂度是n 2 n^2n2的。(知乎上那个也是n 2 n^2n2),但他那个不好降低。

优化,通过哈希的方式来降低时间复杂度。也可以使用平衡二叉树。或者是平衡二叉树和哈希的结合。

平衡二叉树的,插入,删除,查找都是l o g ( n ) log(n)log(n)的。因此也不复杂度就被降到了O(nlogn)

#include

using namespace std;

int main() {

int a[] = { 1,2,3,1,2,4 };

int b[] = { 0,0,0,0,0,0 };

int tmp = a[0], i, j;

for (i = 1; i < 7; ++i)

tmp ^= a[i];

for (i = 0; i < 7; ++i) {

for (j = 0; j < i; ++j) { if (b[j] == a[i]) { cout << a[i] << " " << (tmp ^ a[i]) << endl; break; } }

if (j != i) break;

b[i] = tmp ^ a[i];

}

system("pause");

}

如果以后想到再继续补充吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值