大一计算机的自学总结:异或运算

发布于:2025-02-10 ⋅ 阅读:(53) ⋅ 点赞:(0)

前言

异或运算这个操作看上去很匪夷所思,实际上作用非常大。

一、异或运算的性质

1.异或运算就是无进位相加。

2.满足交换律、结合律。

3.0^n=n,n^n=0。

4.若集合B为集合A子集,集合A异或和为x,集合B异或和为y,则集合A-B异或和为x^y。

#include<bits/stdc++.h>
using namespace std;

//打印二进制
void printBinary(int n)
{
	for(int i=15;i>=0;i--)
	{
		cout<<((n&(1<<i))==0?"0":"1");
	}
	cout<<endl;
}

int sumOfEor(vector<int>arr,int l,int r)
{
	int sum=arr[l];
	for(int i=l+1;i<=r;i++)
	{
		sum^=arr[i];
	}
	return sum;
}

int main()
{
	int n=78;
	cout<<"n in binary:";
	printBinary(n);
	
	//性质
	cout<<"0^n:";
	printBinary(0^n);
	cout<<"n^n:";
	printBinary(n^n);
	
	vector<int>arr(10);
	for(int i=0;i<10;i++)
	{
		cin>>arr[i];
	}
	
	//性质4
	cout<<"sum of eor in 0~7:";
	cout<<sumOfEor(arr,0,7)<<endl;
	cout<<"sum of eor in 8~9:";
	cout<<sumOfEor(arr,8,9)<<endl;
	cout<<"sum of eor in 0~9:";
	cout<<sumOfEor(arr,0,9)<<endl;
	int result=sumOfEor(arr,0,7)^sumOfEor(arr,8,9);
	cout<<result<<endl;
	
	//交换两数
	int a,b;
	cout<<"a,b:"<<endl;
	cin>>a>>b; 
	a=a^b;
	b=a^b;
	a=a^b;
	cout<<"a,b:"<<endl;
	cout<<a<<" "<<b;
	
	return 0;	
} 

二、相关题目

1.获取最大值

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 获取最大值
     * @param a int整型 
     * @param b int整型 
     * @return int整型
     */
    int sign(int n)
    {
        return (n>>31)&1^1;
    }

    int getMax(int a, int b) {
        // write code here
        int c=a-b;
        int signA=sign(a);
        int signB=sign(b);
        int signC=sign(c);
        int diffAB=signA^signB;//判断符号是否一样
        int sameAB=diffAB^1;
        int returnA=diffAB*signA+sameAB*signC;
        int returnB=returnA^1;
        return a*returnA+b*returnB;
    }
};

 为了防止a-b这一步发生数据溢出,可以做以下操作:

首先不管三七二十一先算出a-b,然后分别取a,b,c的符号。这里取符号函数是让该数右移31位后,&1取此时最后一位,即第31位符号位的数,然后^1,若负数返回0,正数返回0。

之后判断a,b符号是否不相同并用变量记住信息,若不同则为1,然后^1就是符号是否相同。

然后,进行分类讨论。若a,b符号不同且a为负数,则b为正数,是最大值,returnA为0;或者若a,b符号相同且c为负数,则此时b也为最大值,否则a为最大值。

最后,按照returnA和returnB的信息来确定返回值。

这里的重点是用变量存储信息的思路!!!

2.丢失的数字

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int sum=0;
        for(int i=1;i<=nums.size();i++)
        {
            sum^=i;
        }
        int sumNums=0;
        for(int i=0;i<nums.size();i++)
        {
            sumNums^=nums[i];
        }
        return sum^sumNums;
    }
};

 根据上述性质4,若已知整体0~n的异或和和数组异或和,只要将两者异或和求异或即为缺失数。

3.只出现一次的数字

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int num=0;
        for(int i=0;i<nums.size();i++)
        {
            num^=nums[i];
        }
        return num;
    }
};

 如果只有一个数字出现一次,其他数字出现两次,根据上述性质3,两相同数的异或结果为0,所以只需要求数组异或和即为只出现一次的数。

4.只出现一次的数字 III

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        vector<int>arr(2);
        long eor1=0;
        for(int i=0;i<nums.size();i++)
        {
            eor1^=nums[i];
        }

        int rightOne=eor1&(-eor1);

        int eor2=0;
        for(int i=0;i<nums.size();i++)
        {
            if((nums[i]&rightOne)==0)
            {
                eor2^=nums[i];
            }
        }
        arr={(int)eor2,(int)eor1^eor2};
        return arr;
    }
};

若有两种数出现一次,思考此时两数的二进制以及数组异或和的二进制特征,可以发现(雾),因为两数不同,所以两数至少有一位的二进制状态不同,则数组异或和的二进制必存在一个1。

Brian-Kernighan算法:取一个数二进制最右侧1的状态——n&(-n)

根据这一位是否是1,可以将数组划分成两部分,所以只出现一次的这两种数必分别位于这两部分,又因为其他数都出现两次,所以将这一位为0的数求异或和即为两数中其中一位,根据性质3,eor1^eor2即为另一个数。

这个题已经有点考验思路了,思考的部分比较难想了。

 5.只出现一次的数字 II

推广:数组中只有一个数出现少于m次,其他m次,返回这个数。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        vector<int>cnts(32,0);
        for(int i=0;i<nums.size();i++)
        {
            for(int j=0;j<32;j++)
            {
                cnts[j]+=(nums[i]>>j)&1;
            }
        }

        int ans=0;
        for(int i=0;i<32;i++)
        {
            if(cnts[i]%3!=0)
            {
                ans|=1<<i;
            }
        }
        return ans;
    }
};

这个题就更考验思维了,从二进制每个数位来考虑,统计每个数位上1出现的次数之和,若为m的倍数,则说明这个数在这位上为0;若%m!=0,则说明这个数在这位上为1。

之后开个32大小的数组存次数,最后根据次数是否为m的倍数往ans里填1即可。

总结

异或运算在解决某些问题时会有奇效,运用得当的话会非常厉害,只要能想出思路(汗)。

END


网站公告

今日签到

点亮在社区的每一天
去签到