异或运算相当与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

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

  1. C语言异或交换两个数的原理解析-大厂面试题:不使用第三变量交换两个数的值

    常用操作交换两个数的值 常用的交换两个数值的方法一般是采用第三个变量,这种方法简单易懂,用代码举例 int main() {//交换两个整形变量int a = 3;int b = 5;int c = ...

  2. 【java】java 如何不使用第三个变量来交换两个数的值

    文章目录 1.概述 2.方案 2.1 算术运算 2.2 位运算 1.概述 今天是一个悲伤的一天,我的悲伤逆流成河.非常非常的悲伤.主要是有这个一个面试题.如何不使用第三个变量来交换两个数的值. 考官说 ...

  3. 用按位异或运算符交换两个数,不引入第三个变量

    2019独角兽企业重金招聘Python工程师标准>>> 用按位异或运算符交换两个数,不引入第三个变量 void swap(int &a , int &b) { a = ...

  4. 利用位运算和指针实现的交换两个数的程序

    位运算是C语言的一大特色,利用异或运算可以实现交换两个数,原理是一个整数与另外一个数进行两次异或运算仍然是其本身,基本原理用式子表达如下: (1) A ^ A = 0; (2) A = A ^B; ( ...

  5. c语言用指针两个字母交换,c语言指针基础之用指针交换两个数(代码实例)

    用指针交换两个数: void swap(int *p,int *q) { int temp; temp = *p; *p = *q; *q = temp; } int main() { int a = ...

  6. 使用java交换两个数——CSDN博客

    /*** * @Title: test_exchange* @Description: 该方法的主要作用:交换两个数* @param 设定文件 * @return 返回类型:void * @throw ...

  7. 不允许创建临时变量,交换两个数的内容

    不允许创建临时变量,交换两个数的内容 #include <stdio.h> int main(int argc, char *argv[]) { int a = 10, b = 100; ...

  8. C语言拆分一个三位数。以及交换两个数(常规法复习加指针法学习)

    给一个三位数,要求拆分出百位十位,个位.然后输出. 以前我们在水仙花那次求水仙花数 提到过经典的取法 我们再次展示一下: #include<stdio.h> int main() {int ...

  9. C语言实现交换两个数

    方法一:最普通方法--建立临时变量 思路分析:建立一个临时变量,通过temp=a,a=b,b=temp来实现交换. 缺点:这只是一种假交换,由于这只是在函数内部临时变量间的交换,所以当函数退出,函数栈 ...

最新文章

  1. 特质波动率python
  2. C#调用C++ dll类型对照表汇总
  3. paramiko连接方式
  4. Python-Jenkins助力传统发布流程
  5. ExtJs2.0学习系列(6)--Ext.FormPanel之第三式(ComboBox篇)
  6. 机器人收集 + Keypose 算法:准确估计透明物体的 3D 姿态
  7. 内存分配——栈、堆、静态区、符号区等等
  8. 算法7-9:有向图搜索算法
  9. A+B and A*B problem 大数相加 相乘 模拟
  10. win98访问win7方法
  11. Labview 中AniGIF控件及注册方法
  12. 内存泄漏VLD工具使用
  13. 在线作图p图|图片生成|做图HTML源码
  14. 心中有佛,看谁都是佛;心中有屎,看谁都是屎。
  15. 超详细纯前端导出excel并完成各种样式的修改(xlsx-style)
  16. oracle数据库如何计算周数,“Oracle”数据库的“周数计算”
  17. 设计模式分析——迭代器模式(Iterator)
  18. 六角螺栓尺寸规格参照
  19. 银河麒麟系统,光盘追加刻录失败
  20. JavaScript 正则表达式实现表单验证

热门文章

  1. 关于我的知识星球服务
  2. Java四种线程池的使用
  3. 【错误解决】[Maven] cannot be opened because it does not exist错误[文件无法编译到target目录下的解决方法]...
  4. Ubuntu使用VNC运行基于Docker的桌面系统
  5. Java探索之旅(6)——对象和类
  6. 安装office时,提示某项注册表无法写入,请查看是否有管理员权限
  7. HDU 1042 N!(高精度阶乘、大数乘法)
  8. 性能测试一些相关的概念
  9. C#网络编程(订立协议和发送文件) - Part.4
  10. 2.2 PostgreSQL 概念