[笔试题]交换两个数不使用第三方变量 深入理解按位异或运算符

异或运算相当与mod 2运算:

1^1 = 0, 1^0 = 1, 0^1= 1, 0 ^ 0 = 0

(1+1)%2 = 0, (1+0)%2 = 1, (0+1)%2 = 1, (0+0)%2 = 0

Single Number II 这题从所有出现3次的数组中找只出现一次的数,相当于mod 3运算

以下部分为转载:

转载:http://blog.csdn.net/kingoverlord/article/details/8825721

不使用第三方变量交换两个变量的值这需要进行位操作,必较麻烦的, 
在学习程序语言和进行程序设计的时候,交换两个变量的值是经常要使用的。通常我们的做法是(尤其是在学习阶段):定义一个新的变量,借助它完成交换。代码如下: 
int a,b; 
a=10; b=15; 
int t; 
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=10;b=10 
通过以上运算,a和b中的值就进行了交换。表面上看起来很简单,但是不容易想到,尤其是在习惯标准算法之后。 
它的原理是:把a、b看做数轴上的点,围绕两点间的距离来进行计算。 
具体过程:第一句“a=b-a”求出ab两点的距离,并且将其保存在a中;第二句“b=b-a”求出a到原点的距离(b到原点的距离与ab两点距离之差),并且将其保存在b中;第三句“a=b+a”求出b到原点的距离(a到原点距离与ab两点距离之和),并且将其保存在a中。完成交换。
此算法与标准算法相比,多了三个计算的过程,但是没有借助临时变量。(以下称为算术算法) 

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

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

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

 

 

 

 

深入理解按位异或运算符
2009-02-10 17:25:14
标签:休闲 按位异或运算符 职场
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://kapok.blog.51cto.com/517862/129941
参与运算的两个值,如果两个相应bit位相同,则结果为0,否则为1。
即:
   0^0 = 0, 
      1^0 = 1, 
      0^1 = 1, 
      1^1 = 0

例如:10100001^00010001=10110000

按位异或的3个特点:
(1) 0^0=0,0^1=1  0异或任何数=任何数
(2) 1^0=1,1^1=0  1异或任何数-任何数取反
(3)              任何数异或自己=把自己置0
                 
按位异或的几个常见用途:
(1) 使某些特定的位翻转
    例如对数10100001的第2位和第3位翻转,则可以将该数与00000110进行按位异或运算。
       10100001^00000110 = 10100111

(2) 实现两个值的交换,而不必使用临时变量。
    例如交换两个整数a=10100001,b=00000110的值,可通过下列语句实现:
    a = a^b;   //a=10100111
    b = b^a;   //b=10100001
    a = a^b;   //a=00000110

(3) 在汇编语言中经常用于将变量置零:
    xor   a,a

(4) 快速判断两个值是否相等
    举例1: 判断两个整数a,b是否相等,则可通过下列语句实现:
        return ((a ^ b) == 0)
    
    举例2: Linux中最初的ipv6_addr_equal()函数的实现如下:
    static inline int ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2)
    {
        return (a1->s6_addr32[0] == a2->s6_addr32[0] &&
            a1->s6_addr32[1] == a2->s6_addr32[1] &&
            a1->s6_addr32[2] == a2->s6_addr32[2] &&
            a1->s6_addr32[3] == a2->s6_addr32[3]);
    }
    
    可以利用按位异或实现快速比较, 最新的实现已经修改为:
    static inline int ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2)
    {
    return (((a1->s6_addr32[0] ^ a2->s6_addr32[0]) |
        (a1->s6_addr32[1] ^ a2->s6_addr32[1]) |
        (a1->s6_addr32[2] ^ a2->s6_addr32[2]) |
        (a1->s6_addr32[3] ^ a2->s6_addr32[3])) == 0);
    }

本文出自 “kapu” 博客,请务必保留此出处http://kapok.blog.51cto.com/517862/129941

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300805.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软面向初学者的机器学习课程:1.4-机器学习技术

写在前面&#xff1a;最近在参与microsoft/ML-For-Beginners的翻译活动&#xff0c;欢迎有兴趣的朋友加入&#xff08;https://github.com/microsoft/ML-For-Beginners/issues/71&#xff09;机器学习技术构建、使用和维护机器学习模型及其使用的数据的过程与许多其他开发工作流…

四川大学计算机科学与技术专业分数线,2015年四川大学计算机科学与技术硕士考研复试分数线是290分...

2015年四川大学计算机科学与技术硕士考研复试分数线是290分 (4页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;19.90 积分2015 年四川大学计算机科学与技术硕士考研复试分数线是 290 分2015 年四川大…

一个男人逐渐变心的过程。。 | 今日最佳

世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;渣男&#xff0c;实锤&#xff01;↓ ↓ ↓

2013-10-10

为什么80%的码农都做不了架构师&#xff1f;>>> http://wordpress.org/plugins/find-duplicates/ 检测文章的标题是否重复的插件 转载于:https://my.oschina.net/520yzw/blog/167580

arm cpu 超频_树莓派 400 超频方法介绍

对树莓派 400 进行超频非常容易实现。散热上&#xff0c;树莓派 400 设计了一个面积很大的被动散热器&#xff0c;这很有用。无需额外的主动降温就可以应对超频的散热需求。尽管有些用户通过配置&#xff0c;让树莓派 400 超频到了 2.2GHz&#xff0c;但我相信 2GHz 更稳一些。…

Blazor Server 和 WebAssembly 应用程序入门指南

翻译自 Waqas Anwar 2021年3月12日的文章 《A Beginner’s Guide To Blazor Server and WebAssembly Applications》 [1]如果您一直紧跟 .NET 世界的最新发展趋势&#xff0c;那么现在您一定听说过 Blazor。目前在 .NET 社区中有很多关于 Blazor 的宣传&#xff0c;这种宣传最常…

张萍萍山东大学计算机科学系毕业生,并行驰骋,放“码”来战!看先导杯大奖赛上山大风采...

原标题&#xff1a;并行驰骋&#xff0c;放“码”来战&#xff01;看先导杯大奖赛上山大风采近日&#xff0c;山东大学计算机科学与技术学院2018级硕士研究生杨林、2020级硕士研究生李威宇组成的团队获得中科院“先导杯”并行应用大赛人工智能赛“大规模物体检测”和“神经网络…

利用percona-toolkit工具检查MySQL数据库主从复制数据的一致性,以及修复。

利用percona-toolkit工具检查MySQL数据库主从复制数据的一致性&#xff0c;以及修复。一、pt-table-checksum检查主从库数据的一致性pt-table-checksum在MASTER上校验指定库、表&#xff0c;将结果存在一个库表里&#xff0c;复制进程将检验sql传递到slave上再执行一次。通过比…

终于丫你奶奶的浩方了!

使用NTFS权限管理清除浩方对战平台广告 作者:feixing 时间:2005-7-14 18:33:17第 1 楼相信坛子里用到浩方上网玩游戏的人应该不下少数吧&#xff1f;随着浩方越做越大。当然也就随之会多很多我们根本不想要的东西。例如在登录浩方时。下方的广告框。进入浩方时。一个劲弹出的窗…

一个妄想的技术宅, 一个自己挖的核掩体, 一起怪异的死亡事件!!

世界只有3.14 % 的人关注了青少年数学之旅2017年9月10日&#xff0c;美国马里兰Bethesda&#xff0c;一个名叫Maplewood的社区如往日般平静。一位居民正在修理自己车挡风玻璃上的裂缝&#xff0c;突然他听到有人大喊&#xff1a;快出来&#xff01;快出来&#xff01;他往声音传…

bigint在java中用什么表示_为什么说开车最能看出一个人的人品和情商?这几条说的太精辟了...

人们常说&#xff0c;想要看清一个女人&#xff0c;就和她逛一次街&#xff0c;想要认清一个男人&#xff0c;就和他喝一次酒&#xff0c;无论是男人和女人&#xff0c;想要认清一个普通人&#xff0c;通过他开车就能看出来。因为每一次开车&#xff0c;都可以看出一个人的底线…

NET问答: 如何使用 C# 直接从 JSON String 中提取特定值 ?

咨询区 Alon Shmiel&#xff1a;我有一个 string&#xff0c;现在我想从中提取出一些值&#xff0c;格式大概如下&#xff1a;string1:"{\r\n \"id\": \"100000280905615\",\r\n \"name\": \"Jerard Jones\",\r\n \"fir…

计算机Word应用程序,Word2007目录设置_计算机软件和应用程序_IT /计算机_信息

Word2007形成目录的方法步骤/方法1.设置目录样式. 此步骤是整理论文和文章&#xff0c;然后定义和设置多级序列的样式(普通论文需要第一级序列XX字体&#xff0c;几个大小的单词等). 主要用途是word2007的样式区域. 打开word2007&#xff0c;然后在“开始”区域中找到“样式”区…

英国出土1600年前的文物,上面居然刻着简体中文?!

全世界只有3.14 % 的人关注了青少年数学之旅最近&#xff0c;一幅大约1600年前的罗马马赛克壁画在英国考古界引起了轰动。这幅壁画的长度和宽度均为6米&#xff0c;埋藏在英国西伯克郡的Boxford村。据考古专家介绍&#xff0c;算上它在内&#xff0c;类似的马赛克壁画全世界一共…

asp.net控件开发基础(20)

示例代码上面我们讨论了数据绑定控件的做法&#xff0c;但都未涉及到asp.net2.0中数据源控件的用法&#xff0c;让用惯了数据源控件的人可能感觉不适应。这次我们就开始讨论在asp.net2.0中&#xff0c;我们该如何重新定义数据绑定控件。我一直在想&#xff0c;是先讨论数据源控…

Win7 Ubuntu13.04互通(win7下用vbox安装Ubuntu)

2019独角兽企业重金招聘Python工程师标准>>> <p>VirtualBox设置</p> <p>采用的是Bridgehost方式</p> <p><a href"http://static.oschina.net/uploads/img/201310/11213732_gHMq.jpg"><img style"background-…

后端接口重定向_接口自动化面试题,建议收藏!

1、get和post的区别&#xff1f;l http是上层请求协议&#xff0c;主要定义了服务端和客户端的交互规格&#xff0c;底层都是tcp/ip协议l Get会把参数附在url之后&#xff0c;用&#xff1f;分割&#xff0c;&连接不同参数&#xff0c;Get获取资源&#xff0c;post会把参数…

.net core ——利用 roslyn 编译C#代码

Rosyln介绍Rosyln1 是 .NET Core和 .NET 4.6 中 的C# 、VB的编译器&#xff0c;宇宙最强IDE Visual Studio 也是使用其来编译代码的&#xff0c; 基于编译器也是服务的理念&#xff0c;微软开发者把其独立出来&#xff0c;并开源维护&#xff0c;开源地址参见注脚2。在工作流引…

期货市场计算机分析指南在线,期货市场计算机分析指南

39.60定价&#xff1a;55.00(7.2折)(美)勒博&#xff0c;(美)卢卡斯 著&#xff0c;何苗 译/2012-04-01《期货市场计算机分析指南》花费了作者大量的心血&#xff0c;这从书中的内容就可以看出。我所读过的有关技术分析的大部分书未能给各种技巧及随后的交易方法打下坚实的基础…

吃货都是怎么给自己找理由的? | 今日最佳

世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;看得我都饿了↓ ↓ ↓