位运算——活学活用

一、基础概念

异或^

相同为0,相反为1
从而演化出:
0^N=N
N^N=0
a^b=b^a    满足交换律
(a^b)^c=a^(b^c)     满足结合律

二、应用

1、【&】取最右位1

int rightOne = eor & (~eor +1 ); //提取出最右位的1

主要是利用了~eor右边都是1的话,只要~eor+1进位导致都会为0,而且最右的1会被进位标记为0

eor:      1 0 1 0 1 1 0 0
~eor:     0 1 0 1 0 0 1 1
~eor+1:   0 1 0 1 0 1 0 0
——————————————————————————
rightOne: 0 0 0 0 0 1 0 0

2、【^】交换2个数

int a=7,b=20;
a=a^b;
b=a^b;
a=a^b;
print a,b  //a=20,b=7

主要利用异或的交换、结合原则

a=a^b;		a=7^20
b=a^b;		b=(7^20)^20	则 b=7
a=a^b;		a=(7^20)^7		则a=20

当然不用临时变量的还有其它方法
a = a + b; b = a - b; a = a - b;
a = a * b; b = a / b; a = a / b;

2、【^】案例-异常次数1

一个数组int arr[n],n>2里面有一种数是奇数数量存在,其他是偶数数量存在,请输出该值,要求时间复杂度O(n) ,空间复杂度O(1)。

void printOddtimes1(int arr[],int n)
{
	int eor=0;	
	for(int i=0;i<n;i++)
		eor^=arr[i]
	cout << eor <<endl;
}

一堆数字互相异或,利用了交换律和结合律,不用理会顺序,最后偶数数量的数字肯定会N^N=0,所以最后的eor就是奇数数量的值。

3、【^】案例-异常次数2

一个数组int arr[n],n>2里面有两种数是奇数数量存在,其他是偶数数量存在,请输出该值,要求时间复杂度O(n) ,空间复杂度O(1)。
解:
假设这2个数为a,b。参照应用2,能得出eor=a^b,那么怎么分开这2个数,也是能用异或。
首先根据题目这是两种数,那么他肯定不相等,并且不等于0,就可以得出eor这int里面最少有1位(rightOne)是1,我们假设是第3位为1.
由于是eor是a^b出来的,所以这1位一定是0和1,所以在arr里肯定可以分成一半第三位是1的一半第三位是0的。这样其中一半里面,有一个数是奇数数量,其他都是偶数数量(又回到案例-异常次数1)那么只要它们里面互相异或,就能得出a 或者b了。
假设我们得到的是a,那么b=eor^a=(a^b)^a=b
这里有几个难点,上面假设的第三位在代码里面怎么取,取哪位?怎么获得=1的那一半?

void printOddtimes2(int arr[],int n)
{
	int eor=0;	
	for(int i=0;i<n;i++)
		eor^=arr[i]
	int rightOne = eor & (~eor + 1);//第三位在代码里面怎么取?这里用简便的方法拿到一个1
	//而且这里我们选择用该位=1的那一半,因为=0的那一半不好去判断
	int onlyOne = 0;
	for(int i=0;i<n;i++)
	{
		if(1==rightOne&arr[i])//获得=1的那一半,这里=0取0的那一半也行,一样的
			onlyOne^=arr[i]
	}
	cout <<eor <<" and "<<eor^onlyOne <<endl;//eor^onlyOne 获得另一个值
}

这里当然就不确定哪个是a,哪个是b了,当然题目也没要求。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值