文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

面试官:请说出四种不使用第三方变量交换两个变量值的方法

2024-12-02 13:39

关注

哈喽,大家好,我是阿Q。前几天有个小伙伴去面试,被面试官的一个问题劝退了:请说出几种不使用第三方变量交换两个变量值的方法。

问题有点绕,好不容易缕清了面试官的问题,却发现答不上来。一时间尴尬无比,只能硬着头皮说不会。

遇到交换变量值的问题,通常我们的做法是:定义一个新的变量,借助它完成交换。

代码如下:

  1. t = a; 
  2.  
  3. a = b; 
  4.  
  5. b = t; 

但问题的重点是“不使用第三方变量”,那就变得“可爱”起来了。思考过后,抛出以下四种方法来解决该问题:

变量本身交换数值

  1. b = (a + b) - (a = b); 

首先执行 a + b 操作,然后将 b 赋值给 a,则 b = a + b - b = a,这就完成了 ab 的互换操作。

算术运算

如图所示:OA = a; OB = b; AB = b - a;

首先我们把 AB 之间的距离 b - a 赋值给 a,此时 AB = a, OB = b 。

由于要达到 ab 交换的目的,所以 OA 要等于 b,而此时 OA 的距离为 b - a ,所以得将 b - a 赋值给 b ,此时 OA = b, AB = a 。

很容易从图中看出,OB 的距离为 b + a,所以我们只需要将 b + a 赋值给 a 就可以完成两者的交换了。

综上所述,我们的步骤为

  1. int a = 10; 
  2.  
  3. int b = 15; 
  4.  
  5. a = b - a; //b=15;a=5; 
  6.  
  7. b = b - a; //b=10;a=5; 
  8.  
  9. a = b + a; //b=10;a=15; 

该算法只能用于整型类型。

指针地址操作

我们可以把 a 和 b 想象为内存中的地址值,假设 a 为 0x01ff5e70 ,b 为 0x01ff5e90 ,而 b - a 表示两个变量在内存中的储存位置隔了多少个字节。所以我们理论上也可以按算术运算的逻辑来交换两个变量的值。

代码如下(此处是 c 语言):

  1. //其中 a 和 b 都是指针变量,里边存储着10和20的地址 
  2. int *a = new int(10); //a=0x01ff5e70 ,此处代表a中存储的地址 
  3. int *b = new int(20); //b=0x01ff5e90 ,此处代表b中存储的地址 
  4.  
  5. //指针变量相减得到20和10的地址间隔了多少个字节,然后转为指针变量 
  6. a = (int*)(b-a);  //b=0x01ff5e90;a=0x8 
  7. b = (int*)(b-a);  //b=0x01ff5e70;a=0x8 
  8. a=(int*)(b+long(a));//b=0x01ff5e70;a=0x01ff5e90 

b - a = 0x01ff5e90 - 0x01ff5e70 = 0x20,0x20 转换为十进制为 32 位,因为一个 int 占4位,所以这里是 0x8 。

以上只是理论状态下的执行过程,如果直接执行是不能实现交换的。因为上边的代码忽略了一个问题:代码编译之后,变量都是存在内存中的,而内存区都会存在基地址。

基地址可以理解为某块内存的起点。上边的数据都是在基地址的基础上做了偏移。

变量的地址 = 变量的基地址 + 变量的偏移地址

当我们进行 b - a 操作的时候,得到结果为 8 ,然后转化为指针变量的时候就会给 8 自动添加基地址,此时的结果就不是 0x8 了,所以会导致结果错误。

另外,地址运算不能出现负数,即当 a 的地址大于 b 的地址时,b - a < 0 ,系统自动采用补码的形式表示负的位移,也会产生错误。

为了解决这个问题,我们只需要保证 b - a 得到的结果不受基地址的影响即可,所以给出以下解决方案。

  1. int *a = new int(10); 
  2. int *b = new int(20);  
  3. cout << a << "`````"
  4. cout << b << "`````"
  5. if(a < b){ 
  6.  a = (int*)(b-a);  
  7.  cout << a << "`````"
  8.  
  9.  b=(int*)(b-(long(a)&0x0000ffff)); 
  10.  cout << b << "`````"
  11.   
  12.  a=(int*)(b+long(a)); 
  13.  cout << a << "`````"
  14. else { 
  15.  b = (int*)(a-b);  
  16.  cout << b << "`````"
  17.  
  18.  a=(int*)(a-(long(b)&0x0000ffff)); 
  19.  cout << a << "`````"
  20.   
  21.  b=(int*)(a+long(b)); 
  22.  cout << b << "`````"

执行结果:

  1. 0x8dbe70`````0x8dbe90`````0x8`````0x8dbe70`````0x8dbe90````` 

看到这,不知道大家是否真的看懂了。反正我第一次看到这儿时,感觉非常清晰(其实完全没有理解),第二次看的时候懵逼了,完全不懂,所以还得大家仔细思考一下才行。

b=(int*)(b-(long(a)&0x0000ffff)); 指令的精妙之处就在于采用了位运算中的与运算,将 a 和 0x0000ffff 进行与运算后,b - a 的基地址计算结果被屏蔽,只保留了偏移地址的计算结果,也就是我们需要的字节数。

在交换很大的数据类型时,该方法执行速度比算术算法快。因为它交换的是地址,而变量值在内存中是没有移动过的。

位运算

既然上边用到了位运算,那我们再说一种直接通过“异或“完成交换的方法。

简单介绍一下异或的规则:

代码如下

  1. int a=10, b=12;//二进制:a=1010;b=1100; 
  2. a = a^b;//a=0110;b=1100 
  3. b = a^b;//a=0110;b=1010 
  4. a = a^b;//a=1100;b=1010 
  5. System.out.println("a="+ a +",b="+ b); 

执行结果

  1. a=12,b=10 

异或运算能够使数据中的某些位翻转,其他位不变。这就意味着任意一个数与任意一个给定的值连续异或两次,值不变。

简单总结

以上四种方法均实现了不借助第三方变量来完成两个变量值的交换:

以上就是今天的全部内容了,如果你有不同的意见或者更好的idea,欢迎联系阿Q,添加阿Q可以加入技术交流群参与讨论呦!

本文转载自微信公众号「阿Q说代码」,可以通过以下二维码关注。转载本文请联系阿Q说代码公众号。

 

来源:阿Q说代码内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯