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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a19990412/article/details/91346449

简述

在知乎上看到一个题目,解答很有意思,用的是位运算。
这让我觉得位运算有更多的算法可能,但是却还没被我用过。
这种东西都是第一次看,觉得挺牛的,第二次,第三次看的时候就觉得没什么了。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

在这里插入图片描述

#include <iostream>
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做异或的数值存下来。

如下:

在这里插入图片描述

  • 注意到:这个时间复杂度是 n 2 n^2 的。(知乎上那个也是 n 2 n^2 ),但他那个不好降低。
    • 优化,通过哈希的方式来降低时间复杂度。也可以使用平衡二叉树。或者是平衡二叉树和哈希的结合。
    • 平衡二叉树的,插入,删除,查找都是 l o g ( n ) log(n) 的。因此也不复杂度就被降到了O(nlogn)
#include <iostream>
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");
}

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

猜你喜欢

转载自blog.csdn.net/a19990412/article/details/91346449