(转)交换两个变量的值,不使用第三个变量的四种法方

通常我们的做法是(尤其是在学习阶段):定义一个新的变量,借助它完成交换。代码如下:

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

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

上面的算法最大的缺点就是需要借助一个临时变量。那么不借助临时变量可以实现交换吗?答案是肯定的!这里我们可以用三种算法来实现:1)算术运算;2)指针地址操作;3)位运算;4)栈实现。

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=10;b=10

它的原理是:把a、b看做数轴上的点,围绕两点间的距离来进行计算。

具体过程:第一句“a=b-a”求出ab两点的距离,并且将其保存在a中;第二句“b=b-a”求出a到原点的距离(b到原点的距离与ab两点距离之差),并且将其保存在b中;第三句“a=b+a”求出b到原点的距离(a到原点距离与ab两点距离之和),并且将其保存在a中。完成交换。
此算法与标准算法相比,多了三个计算的过程,但是没有借助临时变量。(以下称为算术算法)

缺点:是只能用于数字类型,字符串之类的就不可以了。a+b有可能溢出(超出int的范围),溢出是相对的, +了溢出了,-回来不就好了,所以溢出不溢出没关系,就是不安全。

2) 指针地址操作
因为对地址的操作实际上进行的是整数运算,比如:两个地址相减得到一个整数,表示两个变量在内存中的储存位置隔了多少个字节;地址和一个整数相加即“a+10”表示以a为基地址的在a后10个a类数据单元的地址。所以理论上可以通过和算术算法类似的运算来完成地址的交换,从而达到交换变量的目的。即:

int *a,*b; //假设
*a=new int(10);
*b=new int(20); //&a=0x00001000h,&b=0x00001200h
a=(int*)(b-a); //&a=0x00000200h,&b=0x00001200h
b=(int*)(b-a); //&a=0x00000200h,&b=0x00001000h
a=(int*)(b+int(a)); //&a=0x00001200h,&b=0x00001000h

通过以上运算a、b的地址真的已经完成了交换,且a指向了原先b指向的值,b指向原先a指向的值了吗?上面的代码可以通过编译,但是执行结果却令人匪夷所思!原因何在?

首先必须了解,操作系统把内存分为几个区域:系统代码/数据区、应用程序代码/数据区、堆栈区、全局数据区等等。在编译源程序时,常量、全局变量等都放入全局数据区,局部变量、动态变量则放入堆栈区。这样当算法执行到“a=(int*)(b-a)”时,a的值并不是0x00000200h,而是要加上变量a所在内存区的基地址,实际的结果是:0x008f0200h,其中0x008f即为基地址,0200即为a在该内存区的位移。它是由编译器自动添加的。因此导致以后的地址计算均不正确,使得a,b指向所在区的其他内存单元。再次,地址运算不能出现负数,即当a的地址大于b的地址时,b-a<0,系统自动采用补码的形式表示负的位移,由此会产生错误,导致与前面同样的结果。
有办法解决吗?当然!以下是改进的算法:

if(a<b)
{
a=(int*)(b-a);
b=(int*)(b-(int(a)&0x0000ffff));
a=(int*)(b+(int(a)&0x0000ffff));
}
else
{
b=(int*)(a-b);
a=(int*)(a-(int(b)&0x0000ffff));
b=(int*)(a+(int(b)&0x0000ffff));
}

算法做的最大改进就是采用位运算中的与运算“int(a)&0x0000ffff”,因为地址中高16位为段地址,后16位为位移地址,将它和0x0000ffff进行与运算后,段地址被屏蔽,只保留位移地址。这样就原始算法吻合,从而得到正确的结果。

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

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;

此算法能够实现是由异或运算的特点决定的,通过异或运算能够使数据中的某些位翻转,其他位不变。这就意味着任意一个数与任意一个给定的值连续异或两次,值不变。

4)栈实现。不多解释了,栈和相关函数定义省去。

int exchange(int x,int y)
{
     stack S;
     push(S,x);
     push(S,y);
     x=pop(S);
     y=pop(S);
}

以上算法均实现了不借助其他变量来完成两个变量值的交换,相比较而言算术算法和位算法计算量相当,地址算法中计算较复杂,却可以很轻松的实现大类型(比如自定义的类或结构)的交换,而前两种只能进行整形数据的交换(理论上重载“^”运算符,也可以实现任意结构的交换)。

介绍这三种算法并不是要应用到实践当中,而是为了探讨技术,展示程序设计的魅力。从中可以看出,数学中的小技巧对程序设计而言具有相当的影响力,运用得当会有意想不到的神奇效果。而从实际的软件开发看,标准算法无疑是最好的,能够解决任意类型的交换问题。

时间: 2024-10-06 04:25:16

(转)交换两个变量的值,不使用第三个变量的四种法方的相关文章

交换两个变量的值(不使用第三个变量)

问题:不使用第三个变量(也不能使用特殊函数),以达到交换变量值的目的?如:int a = 9, b = 10; 有什么办法使得他们交换呢? 办法一:拐弯抹角"相加"之方案语句是这样的: a = a + b; // a = 9 + 10 b = a - b; // b = 19 - 10 a = a - b; // a = 19 - 10然后你就会陡然发现,它的值居然交换了!是不是很不可思议?没错,就是这样拐弯抹角的交换了,如果没见过这种方案的还真一时半会想不出其他办法. 办法二:使用不

使用指针交换两个整数的值

#include<stdio.h>void change_1(int a, int b);void change_2(int *a, int *b); int main(){ int a = 10; int b = 20; printf("changed before:"); printf("a=%d\tb=%d\n", a, b); change_1(a,b); printf("changed 1 affter:"); printf

【c语言】 &nbsp; 交换两个整形变量的值(要求不创建临时变量)

如何不创建临时变量,来交换两个整形变量的值 异或运算 #include<stdio.h> int main() { int num1=12,num2=24;     printf("交换前的变量值");     printf("%d,%d",num1,num2);       num1=num1^num2;       num2=num1^num2;       num1=num1^num2;//异或  printf("\n交换后的变量值&qu

算法学习 - 不申请第三个数交换两个整数的值

交换两个数 正常情况我们需要多申请一个变量来交换两个数的值(假设变量为a, b).例子如下: int temp = a; a = b; b = temp; 这样子就可以了.但这终究是一种需要申请临时变量的方法,总要去想想临时变量的名字.下面主要介绍两种方法. 直接运算 这种办法简单明了,也是我最喜欢使用的办法,因为即使是浮点型也可以用这种办法,继续假设两个变量是a和b. a = a+b; b = a - b; a = a - b; 这样就交换了!是不是很简单.一下没懂的可以找个例子去试试. 逻辑

交换两个整型变量的值

#include<stdio.h> #include<stdlib.h> int main() {     int a = 1; int b = 2; int tmp = a; a = b; b = tmp; printf("a = %d b = %d\n",a,b); system("pause"); return 0; } 上面是用创建中间变量的方法交换,下面是不创建中间变量的另一种方法:      a = a+b; b = a-b; a

交换两个变量的值,不使用第三个变量的四种法方

通常我们的做法是(尤其是在学习阶段):定义一个新的变量,借助它完成交换.代码如下:int a,b;a=10; b=15;int t;t=a; a=b; b=t;这种算法易于理解,特别适合帮助初学者了解计算机程序的特点,是赋值语句的经典应用.在实际软件开发当中,此算法简单明了,不会产生歧义,便于程序员之间的交流,一般情况下碰到交换变量值的问题,都应采用此算法(以下称为标准算法). 上面的算法最大的缺点就是需要借助一个临时变量.那么不借助临时变量可以实现交换吗?答案是肯定的!这里我们可以用三种算法来

UPDATE从左向右,变量优先,逐行更新.顺序执行的,可以交换两列之间的值

CREATE TABLE tab_update (id TINYINT,n1 NVARCHAR(30),v1 NVARCHAR(30),s1 NVARCHAR(30)) INSERT INTO tab_update (id,n1,v1,s1) SELECT 1,'天','土豆','章子怡' UNION ALL SELECT 2,'集团','黄瓜','汪峰' UNION ALL SELECT 3,'宇宙','茄子','杰克隽逸' UNION ALL SELECT 4,'海洋','西红柿','刘德华

交换两个数值(值类型,引用类型)

变量可以理解为一块空间,int类型变量,就是创建4个字节单位的空间.一块空间单位,只能存取一个值. 值类型交换数字的方法.首先说一下,值类型:值类型就是存在栈上的.简单的理解下,内存是怎么样分配的呢,静态存储区,就是在程序编译的时候,内存就已经分配好了的,并且存在周期是程序的整个运行周期,全局变量,静态变量等.栈上分配:其实也是在程序的运行的时候,可以分配固定太小的内存,并且可以由操作系统自动回收的功能.既然,值类型,引用类型,我觉得值类型存在的必要是什么,什么都是以对象来实现的,不存在值类型,

交换两个数组的值

★将数组A中的内容和数组B中的内容进行交换.(数组一样大) #include<stdio.h> int main() { int a[10]; int b[10]; int c[10]; int i = 0; printf("请输入第一个数组中的五个数值:\n"); for (i = 0; i < 5; i++) { scanf("%d", &a[i]); } printf("\n"); printf("请输入