程序最美(寻路)

你还在坚持练习你的技术吗?运动员天天训练,音乐家也会演练更难的曲章。你呢?

两个数交换的讨论

两个数交换的讨论

         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则马上返回,否则进行进一步的交换操作。

posted on 2013-08-30 13:33  unixfy  阅读(291)  评论(0编辑  收藏  举报

导航