本话题要讨论的是一道面试题目:交换两个变量的值。两个变量而已,看似再简单不过了,不过一道简单的题目可以使用多种方式来完成, 其中有比较普通的实现, 也有相对高明的实现,虽然是一道简单的题目,但是通过面试者对该题目的认知能力,就可以看出面试者的水平。
重点摘要:
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.}
复制代码
程序运行结果如下:
这个程序同时给出了有效的交换(第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的交换结果:
v2的交换结果:
v3的交换结果:
核心部分就是 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 行为例):
十六进制求和如下所示:
47868c00( v.x)
+ 59682f00( v.y)
结果: a0eebb00( v.x + v.y)
注意这个结果的最高位为 1,结果为负数(十进制值为−1594967296,也就是 v.x 与 v.y 的和已经超过了 int 类型的最大(小)值,发生溢出。
执行接下来的语句:
这个计算就是使用溢出后的值−1594967296减去 v.y( 1500000000),从 int 类型的求差角度来说,结果再一次溢出了,十六进制求差如下所示:
a0eebb00( v.x + v.y)
− 59682f00( v.y)
结果: 47868c00(结果 v.y) 经过两次溢出以后,又再次得出了我们期望的值。同样, swap 方法的第 3 条语句:
a0eebb00( v.x + v.y)
− 47868c00( v.y)
结果: 59682f00(结果 v.x)
也是发生了溢出,不过最后的差值也得出了期望的结果。可以看出,当两个数之和很大(小)时,虽然发生了溢出,不过最后还是阴差阳错地得到了正确的结果。尽管结果正确,这种相加的方式还不算十分可取。
|