两个数交换的讨论
两个数交换的讨论
C语言课中,两个数交换的程序必定会涉及,尤其是在讲解如何传参时。这里我们重点讨论一下两个数交换有哪几种方式,以及其原理是什么。
首先,我们给出几种两个数交换的代码,然后逐一讨论:
#include <iostream> #include <vector> using namespace std; void swap1(int& a, int& b) { int t = a; a = b; b = t; } void swap2(int& a, int& b) { a = a + b; b = a - b; a = a - b; } void swap3(int& a, int& b) { a = a ^ b; b = a ^ b; a = a ^ b; } void swap4(int& a, int& b) { if (&a == &b) { return; } a = a ^ b; b = a ^ b; a = a ^ b; } void swap5(int& a, int& b) { if (a == 0 && b == 0) { ; } else if (a == 0 && b != 0) { a = b; b = 0; } else if (a != 0 && b == 0) { a = 0; b = a; } else { a = a * b; b = a / b; a = a / b; } } void swap6(int& a, int& b) { a = a - b; b = b + a; a = b - a; } void swap7(int& a, int& b) { a = a + b - (b = a); } void swap8(int& a, int& b) { a ^= b ^= a ^= b; } void swap9_three(int& a, int& b, int& c) { c = a + c - (a = b, b = c); } void swap10_n(vector<int>& a) { for (auto i = 0; i < a.size() - 1; ++i) { swap8(a[i], a[i + 1]); } } int main() { return 0; }
1.最常规的做法
swap1是我们最常见也是最直观的做法,就是设定一个临时量来暂存其中一个待交换数值。
2.利用两个数之间的和
swap2是利用了待交换两数的和,具体如下:
a = a + b
b = a – b = (a + b) – b = a
a = a – b = (a + b) – a = b
3.利用异或
根据异或运算的特点:
参数1 |
运算 |
参数2 |
结果 |
0 |
异或 |
0 |
0 |
0 |
1 |
1 |
|
1 |
0 |
1 |
|
1 |
1 |
0 |
参数1相对于参数2来说,如果参数2为0,则参数1不变,如果参数2为1,则参数1翻转。也就是说0保持,1翻转。
进而可以推导出 a ^ a = 0,因为如果都为0则为0,如果都为1,由于翻转也为0
a ^ 0 = a
根据该特性,我们有如下算法:
a = a ^ b
b = a ^ b = (a ^ b) ^ b = a 说明:b ^ b = 0
a = a ^ b = (a ^ b) ^ a = b 说明:a ^ b = 0
同或是异或的相反,也就是说 a 同或 b = ~(a ^ b)
参数1 |
运算 |
参数2 |
结果 |
0 |
同或 |
0 |
1 |
0 |
1 |
0 |
|
1 |
0 |
0 |
|
1 |
1 |
1 |
4.异或的一个漏洞
swap3是在a和b不是内存中的同一个对象的情况适用的,如果a和b都是同一个对象的引用,则:
a = a ^ b = 0
b = a ^ b = 0 ^ 0 = 0
a = a ^ b = 0 ^ 0 = 0
最终,导致 a = b = 0。
所以,我们需要考虑a和b是否同时引用了同一个内存对象,这就有了swap4,检测&a == &b。
其实,每个swap函数都需要先检测待交换的两个数是否引用了同一个内存变量,如果引用了同一个内存变量,则需要立即返回。
5.利用两个数的商
根据swap2的思想:利用两个数的和,我们利用两个数的商。但是由于存在0的情况,所以需要分为四种情况来处理:
a == 0 && b == 0
a == 0 && b != 0
a != 0 && b == 0
a != 0 && b != 0
对于有其中一个变量为0的时候,会导致:
a = a * b = 0 * b = 0
b = a / b = 0 / b = 0
a = a / b = 0 / 0:异常
6.利用两个数的差
原理和利用两个数的和差不多:
a = a – b
b = b + a = b + (a - b) = a
a = b – a = a – (a - b) = b
7.合并
a = a + b – (b = a)
其效果是:b = a
a = a + b – (b = a) = a + b – a = b
8.异或合并
a ^= b ^= a ^= b
等价于:
a ^= b
b ^= a
a ^= b
也就是说:
a = a ^ b;
b = b ^ a = b ^ (a ^ b) = a
a = a ^ b = (a ^ b) ^ a = b
9.三个数交换
3个数交换其实有很多种组合。
3! = 6
a、b、c三个数,如果保持不变,则有:a、b、c
如果其中2个不变,则第三个也不变
如果其中一个不变,则有:a、c、b;c、b、a;b、a、c
如果3个全变,则有:b、c、a;c、a、b
我们这里是采用b、c、a的形式:
c = a + c – (a = b, b = c)
其等价于:
a = b
b = c
c = a + c – c = a
10.n个数的交换
对于n个数的情况,n个数总共有n!个组合。
我们采用b、c、a的形式:遍历n个数,依次交换相邻的两个数即得。
总结
以上我们讨论了2个数如何进行交换,参数传递都为引用传参(交换两个值必须引用传递),但是需要考虑的一点就是两个数不能同时引用同一个内存变量,否则将导致交换失败,原来的值被改的。
所以,不仅仅swap4需要检测&a == &b,每个swap函数都需要检测。如果&a == &b则马上返回,否则进行进一步的交换操作。
(完)
文档信息
·版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
·博客地址:http://www.cnblogs.com/unixfy
·博客作者:unixfy
·作者邮箱:goonyangxiaofang(AT)163.com
·如果你觉得本博文的内容对你有价值,欢迎对博主 小额赞助支持