本话题要讨论的是一道面试题目:交换两个变量的值。两个变量而已,看似再简单不过了,不过一道简单的题目可以使用多种方式来完成, 其中有比较普通的实现, 也有相对高明的实现,虽然是一道简单的题目,但是通过面试者对该题目的认知能力,就可以看出面试者的水平。
重点摘要:
1 通过中间变量交换。
2 通过求和与求差交换。
3 通过异或交换。
通过第 3 个变量
首先,我们给出最简单的方式。
【例】 交换两个变量的值。
package chapter2; 2. 3. public class Swap { 4. public static void main(String[] args) { 5. int x = 5; 6. int y = 10; 7. swap(x, y); 8. System.out.println(x); 9. System.out.println(y); 10. Value v = new Value(5, 10); 11. swap(v); 12. System.out.println(v.x); 13. System.out.println(v.y); 14. } 15. //无效的交换 16. public static void swap(int x, int y) { 17. int temp = x; 18. x = y; 19. y = temp; 20. } 21. //有效的交换 22. public static void swap(Value value) { 23. int temp = value.x; 24. value.x = value.y; 25. value.y = temp; 26. } 27.} 28. 29.class Value { 30. int x; 31. int y; 32. 33. public Value(int x, int y) { 34. this.x = x; 35. this.y = y; 36. } 37.}
程序运行结果如下:
5 10 10 5
这个程序同时给出了有效的交换(第22~26 行)与无效的交换(第16~20 行)。因为形参的改变是无法反作用于实参的,所以不能使用变量交换的方式,而是要使用通过引用来修改其成员变量的方式。这就类似与C / C++的语言中的指针效果。
通过相加的方式
通过第3个临时变量,可以成功交换两个变量的值,不过,有时,面试题目的要求会比较苛刻,就是不允许借助于临时变量。这样,就有些复杂了。
【例】 交换两个变量的值2。
package chapter2; 2. 3. public class Swap2 { 4. public static void main(String[] args) { 5. Value v1 = new Value(5, 10); 6. swap(v1); 7. System.out.println("v1 的交换结果: "); 8. System.out.println(v1.x); 9. System.out.println(v1.y); 10. Value v2 = new Value(1200000000, 1500000000); 11. swap(v2); 12. System.out.println("v2 的交换结果: "); 13. System.out.println(v2.x); 14. System.out.println(v2.y); 15. Value v3 = new Value(-1200000000, -1500000000); 16. swap(v3); 17. System.out.println("v3 的交换结果: "); 18. System.out.println(v3.x); 19. System.out.println(v3.y); 20. } 21. 22. public static void swap(Value v) { 23. v.x = v.x + v.y; 24. v.y = v.x - v.y; 25. v.x = v.x -v.y; 26. } 27.}
程序运行结果如下:
v1的交换结果:
10 5
v2的交换结果:
1500000000 1200000000
v3的交换结果:
-1500000000 -1200000000
核心部分就是swap 方法(第22~26 行),该方法的3 条语句解释为:
v.x = v.x + v.y; // 将 v.x 与 v.y 的和存储在 v.x 中 v.y = v.x - v.y; //v.x – v.y 的值就是以前 v.x 的值,赋值给 v.y v.x = v.x -v.y; //v.x – v.y 的值就是以前 v.y 的值,赋值给 v.x
这样,没有通过临时变量,也同样交换了两个变量的值。也许上面的方法一时不太容易理解,那么可以这样考虑:
int z = v.x + v.y;v.y = z – v.y;v.x = z - v.y;
只不过这里使用另外一个变量z,而上面使用v.x 来存储v.x 与v.y 的和,但是交换的效果是相同的。
注意程序的第10 行与第15 行,v.x 与v.y 的初始值非常大(小),这样一来,当执行swap方法时(以第10 行为例):
v.x = v.x + v.y;
十六进制求和如下所示:
47868c00 ( v.x ) + 59682f00 ( v.y ) 结果: a0eebb00 ( v.x + v.y )
注意这个结果的最高位为1,结果为负数(十进制值为1594967296,也就是v.x 与v.y 的和已经超过了int 类型的最大(小)值,发生溢出。
执行接下来的语句:
v.y = v.x - v.y;
这个计算就是使用溢出后的值1594967296减去v.y(1500000000),从int 类型的求差角度来说,结果再一次溢出了,十六进制求差如下所示:
a0eebb00 ( v.x + v.y ) 59682f00 ( v.y ) 结果: 47868c00 (结果 v.y )
经过两次溢出以后,又再次得出了我们期望的值。同样,swap 方法的第3 条语句:
v.x = v.x -v.y;
a0eebb00 ( v.x + v.y ) 47868c00 ( v.y )
结果:59682f00(结果v.x)
也是发生了溢出,不过最后的差值也得出了期望的结果。可以看出,当两个数之和很大(小)时,虽然发生了溢出,不过最后还是阴差阳错地得到了正确的结果。尽管结果正确,这种相加的方式还不算十分可取。
通过异或的方式
位异或运算符(^)有这样一个性质,就是两个整型数据x 与y,有:
(x ^ y ^ y) == x
这说明,如果一个变量x 异或另外一个变量y 两次,结果为x。通过这一点,可以实现交换变量的值。
【例】交换两个变量的值3。
-
1.package chapter2; 2. 3. public class Swap3 { 4. public static void main(String[] args) { 5. Value v = new Value(5, 10); 6. swap(v); 7. System.out.println(v.x); 8. System.out.println(v.y); 9. } 10. 11. public static void swap(Value v) { 12. v.x = v.x ^ v.y; 13. v.y = v.x ^ v.y; 14. v.x = v.x ^ v.y; 15. } 16.}
程序运行结果如下:
10 5
swap 方法(第11~15 行)并不复杂,只有3 条语句:
v.x = v.x ^ v.y; (1) v.y = v.x ^ v.y; (2) v.x = v.x ^ v.y; (3)
可以这样看:执行(1)后,v.x 的值就是v.x(异或运算之前的值)与v.y 异或后的结果。类似数学上的代入法,将v.x(值为v.x ^ v.y)代入(2)中,于是(2)赋值运算符(=右侧的表达式相当于:
v.x ^ v.y ^ v.y //v.x与v.y都是运算之前的值
这个值就是v.x(异或运算之前的值)、然后赋值给左侧的v.y。此时,v.y 的值就是异或运算之前v.x 的值。然后将v.x(值为v.x ^ v.y)、v.y(值为异或运算之前v.x 的值)代入(3)中赋值运算符右侧,代入后表达式相当于:
v.x ^ v.y ^ v.x //v.x与v.y都是运算之前的值
。推荐:http://www.lemonpai.com/1508.html
这个值就是v.y,然后赋值给v.x,至此,完成两个变量的交换。异或方式要比相加方式更加可取,因为异或运算不涉及溢出。综合这3 种方式而言,第1 种方式最容易理解,第3 种方式相对成熟老练,如果是个人编写程序,建议还是使用最简单的方式,这样不容易出错,而且代码具有可读性,便于后期的维护,如果是在面试过程中,那就可以选择第3 种方式来证明下自己的能力。
总结:
-
1. 一个变量x 异或另一个变量y 两次,结果的值为x。
2. 异或运算可以交换两个变量的值,并且这种方式比相加交换的方式更可取些。
举一反三
既然相加(第2 种方式)能够实现交换两个变量的值,那么相减肯定也能实现。编写一个程序,不要借助第3 个临时变量,实现相减操作交换两个变量的值。
本文作者梁勇出自柠檬派,请注明出处
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/191616.html