zoukankan      html  css  js  c++  java
  • 不使用临时变量交换两个整数

    最普通的方法用临时变量的方法就不说了,下面的没有用临时变量:

    int main()
    {
        int a,b;
        
        while(scanf("%d%d",&a,&b)!=EOF)
        {
        
            a=a+b;
            b=a-b;
            a=a-b;
    
            printf("%d	%d
    ",a,b);
        }
         
    }

    有些人说当a,b比较大时会溢出产生错误的结果。其实不正确,溢出虽然会溢出,但是结果任然正确。为什么?

    我们假设

    a,b都是short int(int类似)。

    a 0x7fff 32767(2^15-1,有符号整形所能表示的最大范围)。

    b 0x7fff 

    a=a+b后为0xfffe;(如果看成有符号数为-2(0xffff为-1)。看出无符号数为65534)。因为为signed,现在溢出了。

    b=a-b; -1-32767=-32768再次溢出变成最大值32767

    b=a-b类似。

    可以看到,会有2次溢出。溢出规律:

    SHRT_MAX+1 == SHRT_MIN
    SHRT_MIN-1 == SHRT_MAX

     参考:http://bbs.csdn.net/topics/390335866?page=1#post-393371903

    但是结果正确。我们要劲量避免溢出。

    采用异或运算:

    a=a^b;
    b=a^b;
    a=a^b;

    (可以写成一句:b ^= a ^= b ^= a;  )

    首先来看第一句:a=a^b;执行该语句后a中保存了a与b的差异位,也就是说如果原来的a和b的某一位不同,那么就将a的该位置为1,因此a在内存中成了如下图的样子,它说明a与b的第2,3个bit有差异:

    接着我们来看第二句:b=b^a;其意思是,将b中有差异的位翻转,如此一来b中保存的值其实就等于原来a中的值,记住当第二个语句执行完之后a仍然保存了原来的a,b的差异信息,而b则变成了原来的a!

    最后我们来看第三句:a=a^b;由于异或运算满足交换律,因此这一句等价于:a=b^a;记住这个语句赋值号右边的b中已经保存了原始的a值,而a中保存了原始的a,b的差异,因此这一句的最终作用是将原始a中有差异的位翻转(变成b)然后赋值给a,如此一来a中就保存了原始的b值。

    总结:上述三句中:第一句是记录差异,第2,3句是翻转,最终实现了不用任何中间变量就交换两个变量的值

    可以看到右边的都是一样a^b;

    举例:

    a 0x7fff

    b 0x7ffe

    a=a^b 0x0001;

    b=a^b ; 结果0x7fff

    a=a^b; 结果0xfffe

    交换成功。

    说明一下,不要使用异或运算,增加了复杂度。

    通过异或交换2个数的一个小陷阱;

    可用通过异或运算交换两个数,而不需要任何的中间变量。 如下面:

    1. void exchange(int &a, int &b)
    2. {
    3.     a ^= b;
    4.     b ^= a;
    5.     a ^= b;
    6. }


    然而,这里面却存在着一个非常隐蔽的陷阱。

    通常我们在对数组进行操作的时候,会交换数组中的两个元素,如exchang(&a[i], &b[j]), 这儿如果i==j了(这种情况是很可能发生的),得到的结果就并非我们所期望的。

    1. void main() 
    2. {
    3.    int a[2] = {1, 2};
    4.    exchange(a[0], a[1]); //交换a[0]和a[1]的值
    5.    printf("1---a[0]=%d a[1]=%d ", a[0], a[1]);
    6.    exchange(a[0], a[0]); //将a[0]与自己进行交换
    7.    printf("2---a[0]=%d a[1]=%d ", a[0], a[1]);
    8. }

     上面那段测试代码的输出是:

    1. 1---a[0]=2 a[1]=1
    2. 2---a[0]=0 a[1]=1

    很意外吧,第一次的交换正确的执行了,但是第二次调用exchange的时候却将a[0]置为了0. 仔细分析,不难发现,这正是我们在exchange里面用异或实现交换所造成的。如果输入a和b是同一个数,exchange里面代码相当于:

    1. a ^= a;
    2. a ^= a;
    3. a ^= a;

    成了a做了3次(a^a^a^a^a)于自己的异或,其结果当然是0了。

    既然这样,我们就不能够在任何使用交换的地方采用异或了,即使要用,也一定要在交换之前判断两个数是否已经相等了,如下:

      1.     void exchange(int &a, int &b)
      2.     {
      3.         if(a == b) return; //防止&a,&b指向同一个地址;那样结果会错误。
      4.         a ^= b;
      5.         b ^= a;
      6.         a ^= b;
      7.     }
      8.  

    用异或来交换两个变量是错误的。参考这篇文章批判了。

  • 相关阅读:
    用于爬取知乎某个话题下的精华问题中所有回答的爬虫
    BSP -- 图书共享系统(Book Sharing Platform)
    【已解决】WPS2018 从第三页开始插入页眉页码(即前两页不要页眉页码)
    【编译原理】大白话讲解 First 集和 Follow 集的构造算法
    如果
    HTTP协议(1)------->资源和URL
    JavaWeb框架_Struts2_(八)----->Struts2的国际化
    深入理解java虚拟机----->垃圾收集器与内存分配策略(下)
    深入理解java虚拟机----->垃圾收集器与内存分配策略(上)
    JavaWeb框架_Struts2_(七)----->文件的上传和下载
  • 原文地址:https://www.cnblogs.com/youxin/p/3235600.html
Copyright © 2011-2022 走看看