异或运算的各种操作

异或运算可以理解为无进位相加
那么就不难理解,异或运算满足交换律跟结合律,即结果与异或顺序没有关系
重要结论 N^N=0

例题

在这里插入图片描述

假设白为1,黑为0,那么袋子里相当于a个0,b个1;
设c=a^b,把这个结果放入袋子里,再重复操作;
那么最后剩下的那个数,就是袋子中全部数字的异或结果
在这里插入图片描述

1.交换两数

利用性质 N^N=0

	int a = 10, b = 20;
	a = a ^ b;
	b = a ^ b;  //b=a^b^b=a
	a = a ^ b;  //a=a^b^a=b
	printf("%d %d", a, b);

使用需要注意如果进行数组内容交换,下标不能相同

2.不使用判断语句返回两个数的最大值


int sign(int n)  //取出二进制符号位
{
	return (n >> 31) & 1 ^ 1 ;  //非负1,负数0
}
int flip(int n)
{
	return n ^ 1; //1变0,0变1
}

int getmax1(int a, int b) //a-b的差值c有溢出风险
{
	int c = a - b;
	//c非负,returnA为1 ,returnB为0
	//c负数,     则为0,        为1
	//即只有一个1和一个0
	int returnA = sign(c);
	int returnB = flip(returnA);
	return returnA * a + returnB * b;
}

int getmax2(int a, int b)  //没有风险的实现
{
	int c = a - b; //c仍可能溢出
	//a,b,c的符号与1异或值  非负1,负数0
	int sa = sign(a);
	int sb = sign(b);
	int sc = sign(c);
	
	int diffAB = sa ^ sb;  //判断a和b符号是否一样,同号为0.异号1
	int sameAB = flip(diffAB); //与以上结果正好互斥!

	//一.a b符号不同且a非负  二.a b符号相同且c非负(a>b)  满足两个返回a
	int returnA = diffAB * sa + sameAB * sc;
	int returnB = flip(returnA);
	return returnA * a + returnB * b;

}

3.找到缺失的数字

这个简单一些

int missingNumber(int* nums, int numsSize)
{
    int x=0;
    for(int i=0;i<numsSize;i++)
    {
        x^=nums[i];
    }

    for(int j=0;j<numsSize+1;j++)
    {
        x^=j;
    }
    return x;
}

4.数组中1种数出现了奇数次,其他的数都出现了偶数次,返回出现了奇数次的数

这是力扣136题,(不需要额外空间的方法,就往位运算上想–来自评论区)

	int singleNumber(int* nums, int numsSize)
{
    int eor=0;
    for(int i=0;i<numsSize;i++)
    {
        eor^=nums[i];
    }
    return eor;
}

Brian Kernighan算法 - 提取出二进制状态中最右侧的1

一个数取反+1就是它的相反数 (~n)+1
再把原数字与上它 n&((~n)+1)
就是 n&(-n)

5.数组中有2种数出现了奇数次,其他的数都出现了偶数次,返回这2种出现了奇数次的数

设这两数为a,b
eor1=a^b
令rightOne=eor1&(-eor1)
那么提取出eor1二进制状态中最右侧的1后,只有两种情况,
那就是在那一位上为1或为0的数
第一种情况为一组数异或rightOne后为0,把他赋给eor2,假设eor2是a
第二种情况只需eor1^eor2=a ^ b ^ a=a

int* singleNumber(int* nums, int numsSize, int* returnSize) 
{
    long eor1=0;  //用long是因为negation of -2147483648 cannot be represented in type 'int'
    for(int i=0;i<numsSize;i++)
    {
        eor1^=nums[i]; 
    }
    //eor1=a^b
    int eor2=0;
    int rightOne=eor1&(-eor1);
    for(int i=0;i<numsSize;i++)
    {
        if((rightOne & nums[i])==0)
        {
            eor2^=nums[i];
        }
    }
    int*ans=(int*)malloc(8);
    ans[0]=eor2;
    ans[1]=eor1^eor2;
    *returnSize=2;
    return ans;
}


6.数组中只有1种数出现次数少于m次,其他数都出现了m次,返回出现次数小于m次的那种数

力扣137题,题中m=3
在二进制上,一个数出现m次,它二进制形式里的每个位上的1和0都会出现m次
我们只考虑1,对每个位上的1累加,对于每个累加和
%m后如果是0,那么那个出现次数少于m次的数的二进制形式在这个位上是0,反之为1
最后,要实现二进制补1,只需进行或运算,且需要注意使用1u而不是1

int singleNumber(int* nums, int numsSize) {
    //cnts[i] : i位上有多少个1
    int cnts[32]={0};
    for(int i=0;i<32;i++)
    {
        for(int j=0;j<numsSize;j++)
        {
            cnts[i]+=(nums[j]>>i)&1;  //向右移动i位
        }
    }
    unsigned int ans=0;
    for(int i=0;i<32;i++)
    {
        if((cnts[i])%3 !=0)
        {
            ans|=(1u<<i);  //必须是1u而不能是1,因为1u可以处理负数
        }
    }

    return ans;
}

另外附上力扣189轮转数组:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
在这里插入图片描述
原数组中 在k=3的位置处,4和5相连
轮转后,7和1相连
不难得出方法:将数组左边和右边分别旋转,就能使7和1相连
最后对数组整体旋转从而纠正顺序

void reverse(int*nums,int left,int right)
{
    while(left<right)
    {
        int tmp=nums[left];
        nums[left]=nums[right];
        nums[right]=tmp;
        right--;left++;
    }
}

void rotate(int* nums, int numsSize, int k) 
{
    if(k)
    {
        k=k%numsSize;
        reverse(nums,0,numsSize-k-1);
        reverse(nums,numsSize-k,numsSize-1);
        reverse(nums,0,numsSize-1);
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值