两个整形数交换的算法,不使用其它变量

本文深入探讨了不借助临时变量实现变量值交换的四种算法:算术运算、指针地址操作、位运算及栈实现。通过对比分析,揭示了每种算法的优缺点,特别强调了在不同场景下的适用性和执行效率。本文旨在为程序员提供更加灵活多样的变量交换解决方案。

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

int a=10;
int 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=12;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); 
}

以上算法均实现了不借助其他变量来完成两个变量值的交换,相比较而言算术算法和位算法计算量相当,地址算法中计算较复杂,却可以很轻松的实现大类型(比如自定义的类或结构)的交换,而前两种只能进行整形数据的交换(理论上重载“^”运算符,也可以实现任意结构的交换)。
介绍这三种算法并不是要应用到实践当中,而是为了探讨技术,展示程序设计的魅力。从中可以看出,数学中的小技巧对程序设计而言具有相当的影响力,运用得当会有意想不到的神奇效果。而从实际的软件开发看,标准算法无疑是最好的,能够解决任意类型的交换问题。

<think>嗯,用户让我用冒泡排序法对15个整从小到大排序。首先,我得回忆一下冒泡排序的基本原理。冒泡排序是通过断比较相邻元素,如果顺序对就交换它们,这样每一轮会把最大的元素“冒”到末尾。 那具体步骤呢?假设有一个组,长度是15。外层循环控制需要多少轮比较,因为每一轮确定一个最大的,所以对于n个元素,需要n-1轮。内层循环负责每一轮中的相邻比较,每轮比较的次应该是当前未排序的元素减一,也就是n-1-i次,i是已经完成的轮。 然后,我需要考虑如何用C语言实现。首先定义组,可能用int arr[15],然后用户输入或者初始化据。这里可能需要用循环输入。然后两层循环,外层从0到14(但实际到14-1=13次),因为15个需要14轮。内层循环每次从0到n-1-i,这里i是外层变量,初始为0,每轮递增。 交换的话,需要一个临时变量temp,交换arr[j]和arr[j+1]。然后打印排序后的组。 过要注意边界条件,比如组越界。比如内层循环的j应该到n-2-i,也就是j+1超过组末尾。比如当i=0时,j从0到13,因为j+1=14是最后一个元素。这样就会越界了。 然后测试代码是否正确,可能需要举例。比如输入15个乱序的,看看是否排序正确。或者用例子组测试,比如反序排列,看是否变成升序。 用户可能需要完整的代码示例,所以我会写出完整的C程序,包括输入和输出部分。比如用for循环输入,然后排序,再for循环输出。 另外,可能要考虑优化,比如如果在某一轮没有发生交换,说明已经有序,可以提前结束循环。过用户的问题可能需要优化,但可以提一下。 然后,用户的问题可能还会涉及到时间复杂度,冒泡排序的平均和最坏情况是O(n²),最好情况是O(n)(如果优化过)。过这里可能需要深入讨论,但可以作为相关问题提出。 最后,检查代码是否正确,比如变量名是否正确,循环条件是否正确,交换逻辑是否正确。确保没有语法错误,比如分号,括号匹配等。</think><< 以下是使用冒泡排序法对15个整进行从小到大排序的C语言实现及详细说明: ```c #include <stdio.h> int main() { int arr[15]; int n = 15; int temp; // 输入15个整 printf("请输入15个整:\n"); for(int i=0; i<n; i++){ scanf("%d", &arr[i]); } // 冒泡排序核心算法 for(int i=0; i<n-1; i++) { // 外层循环控制轮次 for(int j=0; j<n-1-i; j++) { // 内层循环控制比较次 if(arr[j] > arr[j+1]) { // 比较相邻元素 // 交换元素 temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } // 输出排序结果 printf("排序后的组:\n"); for(int i=0; i<n; i++){ printf("%d ", arr[i]); } return 0; } ``` 实现原理: 1. 外层循环(i循环):控制排序轮次。对于n个元素需要n-1轮排序(最后一个元素自然有序) 2. 内层循环(j循环):控制每轮比较次。每轮结束后最后的i+1个元素已排序,因此比较次为n-1-i次 3. 比较交换:通过相邻元素比较,将较大的元素向后移动,实现"冒泡"效果 4. 时间复杂度:O(n²),空间复杂度:O(1) 优化建议: 1. 可以添加flag标记判断是否发生交换,若某轮未交换说明已有序,可提前终止排序 2. 可以使用指针操作替代组下标访问提升效率 3. 可以封装排序函提高代码复用性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值