zoukankan      html  css  js  c++  java
  • 不借助其它变量交换两变量值

    在学习程序语言和进行程序设计的时候,交换两个变量的值是经常要使用的。通常我们的做法是:定义一个新的变量,借助它完成交换。代码如下:

    int a,b,t;
    a=10; b=15;
    t=a; a=b; b=t;

    这种算法易于理解,特别适合帮助初学者了解计算机程序的特点,是赋值语句的经典应用。在实际软件开发当中,此算法简单明了,不会产生歧义,便于程序员之间的交流,一般情况下碰到交换变量值的问题,都应采用此算法(以下称为标准算法)。

    标准算法最大的缺点(其实根本不算缺点)就是需要借助一个临时变量。那么不借助临时变量可以实现交换吗?答案是肯定的!这里我们可以用至少三种算法来实现,他们是:
        1)算术运算
        2)指针操作
        3)位运算
       
    1) 算术运算
    简单来说,就是通过+和-运算来实现。代码如下:

    int a,b;
    a=10;b=12;
    a=b-a;   //a=2;b=12
    b=b-a;   //a=2;b=10
    a=b+a;   //a=12;b=10

    通过以上运算,a和b中的值就进行了交换。表面上看起来很简单,但是不容易想到,尤其是在习惯标准算法之后。

    此算法与标准算法相比,多了三个计算的过程,但是没有借助临时变量。(以下称为算术算法)

    2) 指针操作
    对指针的操作实际上进行的是整数运算。比如:两个int指针相减得到一个整数N,该整数表示两个指针变量在内存中的储存位置隔了N*sizeof(int)个字节;int指针和一个整数相加,例如“a+10”表示以a为基地址,偏移为10*sizeof(int)处的int变量。所以我们完全可以通过和算术算法类似的运算来完成指针变量值的交换,从而达到交换变量的目的。即:

    int *a,*b;    
    a=new int(10);        //给指针赋值
    b=new int(20);        //a=0x00030828,b=0x00030840
    a=(int*)(b-a);        //a=0x00000006
    b=(int*)(b-int(a));   //b=0x00030828
    a=(int*)(b+int(a));   //a=0x00030840

    需要注意的是:最后三句话中,只有第一句是两个指针之间的计算,其他都是指针和整数的计算,否则会导致计算错误,严重导致系统出错。

    通过以上运算a、b的地址就完成了交换,a指向了原先b指向的值,b指向原先a指向的值!

    此算法同样没有使用第三变量就完成了值的交换,与算术算法比较它显得不好理解,但是它有它的优点即在交换很大的数据类型时,比如说自定义的大型结构或者类,它的执行速度比算术算法快。因为它交换的时地址,而变量值在内存中是没有移动过的。(以下称为地址算法)

    3) 位运算
    通过异或运算也能实现变量的交换,这也许是最为神奇的,请看以下代码:

    int a=10,b=12;  //a=1010^b=1100;
    a=a^b;    //a=0110^b=1100;
    b=a^b;    //a=0110^b=1010;
    a=a^b;    //a=1100=12;b=1010;

    此算法能够实现是由异或运算的特点决定的,通过异或运算能够使数据中的某些位翻转,其他位不变。这就意味着一个数a与任意一个给定的数b连续异或两次,a的值不变。即:a^b^b=a。将a=a^b代入b=a^b则得b=a^b^b=a;同理可以得到a=b^a^a=b;轻松完成交换。

    以上三个算法均实现了不借助其他变量来完成两个变量值的交换,相比较而言算术算法和位算法在纯C中仅能完成标准数据类型的交换,对于结构体就无能为力了;而在C++中需要使用重载操作则也可以完成复杂类型的交换;指针算法则可以轻松的实现任意数据结构的交换。

    介绍这三种算法并不是要应用到实践当中,而是为了探讨技术,展示程序设计的魅力。从中可以看出,数学中的小技巧对程序设计而言具有相当的影响力,运用得当会有意想不到的神奇效果。而从实际的软件开发看,标准算法无疑是最好的,能够解决任意类型的交换问题。
  • 相关阅读:
    全网最深分析SpringBoot MVC自动配置失效的原因
    这一次搞懂Spring代理创建及AOP链式调用过程
    这一次搞懂SpringBoot核心原理(自动配置、事件驱动、Condition)
    这一次搞懂Spring Web零xml配置原理以及父子容器关系
    这一次搞懂SpringMVC原理
    这一次搞懂Spring事务是如何传播的
    Redis系列(八):发布与订阅
    【深度思考】JDK8中日期类型该如何使用?
    【Redis面试题】如何使用Redis实现微信步数排行榜?
    Nacos系列(一):Nacos环境安装及Hello World示例
  • 原文地址:https://www.cnblogs.com/fhuafeng/p/1511777.html
Copyright © 2011-2022 走看看