‘^’概论
二目位运算符 两数同一二进制位上相同为为0,相异为1
交换数值
#include<stdio.h>
int main()
{ //a b
int a=9,b=8;//0000 1001 0000 1000
b^=a; //0000 1001 0000 0001
a=^b; //0000 1000 0000 0001
b^=a; //0000 1000 0000 1001
printf("%d\t%d",a,b);
return 0;
}
数学理解
我们将一个二进制数视作一个集合,集合的元素是一个结构体,包含对象为数字(0/1)与位置。当两个取亦或时,即是对两集合的元素标记,但由于标记结果与结构体一致,所以可以将亦或看作是两集合交集在并集中的补集(对1而言)
一、有nums个数,其中只有一个数出现一次,其他数字均出现两次,求出现一次的数。
要求:时间复杂度限制在O(n)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define MAX 5
int main()
{
int x = 0;
arr[MAX] = {1,2,3,4,6}
for (int i = 1; i <= MAX; i++)
{
x ^= arr[i];
}
for (int i = 1; i <= MAX; i++)
{
x ^= i;
}
printf("%d", x);
return 0;
}
取0,与所有数作亦或运算,结果即为所求数
二、有nums个数,其中有两数出现一次,其他数字均出现两次,求两仅出现一次的数
要求:时间复杂度限制在O(n) 空间复杂度限制在O(1)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define MAX 10
int main()
{
int x = 0;
int arr[MAX] = { 1, 3, 3, 4, 6 ,6,9, 9,8,8};
for (int i = 0; i < MAX; i++)//删去已重复数字,获得目标数字的亦或结果
{
x ^= arr[i];
}
int m=1;
while (((x >> m) & 1) != 1)
{
m++;
}
int x1 = 0, x2 = 0;
for (int i = 0; i < MAX; i++)
{
if (((arr[i] >> m) & 1) == 1)//分类
x1 ^= arr[i];
else
x2 ^= arr[i];
}
printf("%d\t%d", x1,x2);
return 0;
}
取0,与所有数作亦或运算,得到两数作亦或运算。此后考虑如何将两数分开
思路一:这是一个排列组合问题,讨论0是由两个0产生还是由两个1产生 排除已重复的元素再进入循环一一判断。
问:是否会重复?会,因为亦或得到的结果实际可以看作是很多有这一序列的不同数亦或后的结果
思路二:假设亦或结果第N位上有为1,以此为分类标准,分为两组AB,则已重复的要么在A,要么在B,而两个数互不相同,一个在A,一个在B,分别对A、B作亦或运算,记得到所求数。
问:如此求得的数是否超过两个以上?对于一个不确定的数集,实际上有无数个数字满足上述条件,而对给定数集,事实上只存在一个。
三、有nums个数其中只有一个数只出现一次,其他数出现三次
要求同上
现在单用‘^’行不通了,但还是考虑使用二进制来思考问题,三个相同的数,那么每个二进制位相加mod3的余数就是答案但二进制无法在一个位上表示相加的结果,可以引入数组计数。也可以重新生成一个2进制数:由于每位运算法则相同。考虑一位 (原:已经过一系列运算后的结果)(增:新的一个数)(果:结果)
原(x) 增(nums[i]) 果(x)
00 0 00
00 1 01
01 0 01
01 1 10
10 0 10
10 1 11(00)
记第一位为one 第二位为 two//以下为one 的分析
if(two==0){
if(nums[i]==0)
one=one;
else {
one=~one
}
else one=0;
综上简化为 if(two==0) one^=nums[i] else one=0==> one^=nums[i] &(~two)
//以下为two的分析
if(one==0)
if(nums[i]==0)
two=two;
else two=~two
else two=0;
故 two与one 遵循相同规则 two^=nums[i] &(~one)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define SIZE 7
int main()
{
int nums[SIZE] = { 1, 3, 4, 9, 3, 9, 1 };
int one = 0;
int two = 0;
for (int i = 0; i < SIZE; i++)
{
one ^= nums[i] & (~two);
two ^= nums[i] & (~one);
}
printf("%d", one);
return 0;
}
例一反思
前提a 0与所有数亦或为该数
前提b 相同两数亦或为0000...0
前提c 亦或满足交换律 由于是位运算,仅考虑一位,显然对任意顺序的0与1交换与否无关紧要
例二方法比较
思路一二本质相同,思路一更为直观,以数学方式进行思考;思路二显然跟适合以程序描述,但不宜想到。
例三方法
程序员专属思维方式