简述
在知乎上看到一个题目,解答很有意思,用的是位运算。
这让我觉得位运算有更多的算法可能,但是却还没被我用过。
这种东西都是第一次看,觉得挺牛的,第二次,第三次看的时候就觉得没什么了。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
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^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");
}
如果以后想到再继续补充吧