本题作为一个被各种面试宝典收录的经典面试题,每个合格的程序员都应该把下面的代码背下来了吧:
a=a+b; b=a-b; a=a-b;
稍难一点的变种题会再加一个限定条件:不使用加减乘除。没关系,面试宝典里也有,我们用位运算代替加减法也能实现:
a=a^b; b=a^b; a=a^b;
这里用了异或操作符。异或公式为 a^b=(~a&b)|(a&~b)。即如果两个数字的某一位(bit)是相异的(一个为1一个为0),那么结果中该位是1(相当于执行了或操作),否则该位为0(同为1或同为0的情况下)。如果a=1010, b=1100,这里ab都是二进制表示,那么结果就为0110。我们把异或记作“异1同0”就好了。
如果某一种操作能结合两个元素a,b的信息得到新元素s,而且该操作还能从s中剔除元素a或b得到b或a(简单点说a,b,s三个元素只用知道任意两个就能取得第三个),那么这种操作就可以帮助我们交换数字。最常见的算法就是+(结合)-(剔除)、*(结合)/(剔除):
a=a+b; b=a-b; a=a-b;
a=a*b; b=a/b; a=a/b; // 虽然计算复杂了点,但是跟上面的操作没任何区别
再来分析异或操作,假如a和b都只有1个bit(值0或1),那么异或操作能得到一个确定的结果s(0或1,也是1个bit),那么如果我们知道s和ab中的一个呢?我们略加思考就能知道另一个:
s=0, a=0 => b=0; // s=0表示ab数字相同,所以b=a=0
s=0, a=1 => b=1;
s=1, a=0 => b=1; // s=1表示ab数字相异,所以b=^a=1
s=1, a=1 => b=0;
上面我们推导出来的4种剔除操作的结果b恰好跟异或s、a得到的结果一致(巧合吗?),那么剔除操作也用异或就可以了。所以得到了a=a^b; b=a^b; a=a^b;这种算法。
如果我们把给定的2个数字,一个当做s一个当做a(之前都是当成a与b然后算出s),操作也是同样可行的
a = s - a; 此时s=s, a=b
s = s - a; 此时s=a, a=b
a = s + a; 此时s=a, a=s,互换成功