异或交换算法中的小坑

突然想起来挺久前的一件事,因为太琐碎就不放到「深夜学算法」系列里了。

「交换两数」大概是编程入门者紧接着Hello World写的程序,常用和知名到都有段子:

// 普通程序员
void normal_swap(int &x, int &y) {
    int temp = x;
    x = y;
    y = temp;
}

// 二逼程序员
void foolish_swap(int x, int y) {
    int temp = x;
    x = y;
    y = temp;
}

通常交换两数都会使用临时变量temp,但是也有不使用的方法,称为异或交换算法(XOR swap algorithm):

void xor_swap(int &x, int &y) {
    x ^= y;
    y ^= x;
    x ^= y;
}

算法本身算是广为人知,原理也很简单。假设x = 3,y = 5,那么分别写成二进制形式就是x = 00000011,y = 00000101

  1. 经过 x ^= y
    x = 00000110,y = 00000101
  2. 经过y ^= x
    x = 00000110,y = 00000011
  3. 经过x ^= y
    x = 00000101,y = 00000011

此时x = 5,y = 3,交换成功。

看起来非常漂亮,但用C/C++实现时,有一个隐藏的小坑。


知道这个交换算法时我正在练习各种排序算法,就直接用在快速排序里了。写完代码一运行,别说排好序了简直比随机数还随机数。

当然作为C/C++学习者,我的第一反应是:

又搞错指针了耶…

但对着教材看了几遍快速排序的实现都没找到错误,用特殊输入试了几次后,我突然发现了问题所在。

交换两数有一个特例:「要交换的两个数指向同一片存储区域」。

什么意思呢?就是说可能会调用swap(x, x),这件事虽然很傻,但调用后应该保证x的值不变。然而上面实现的xor_swap就在这里出了问题。

还是假设x = 3,写成二进制就是00000011:

  1. 经过x ^= y
    x = 00000000
  2. 经过y ^= x
    x = 00000000
  3. 经过x ^= y
    x = 00000000

所以无论x原来的值是什么,调用swap(x, x)后都变成0了。

好一个すべてがゼロになる(全部成为0)!

解决起来很简单,判断一下两个数是否相等,相等不用交换,不相等肯定不指向同一片存储区域。

所以正确的异或交换算法实现应该是:

void xor_swap(int &x, int &y) {
    if(x != y) {
        x ^= y;
        y ^= x;
        x ^= y;
    }
}

这件事情的启示有两条:

  1. 遇事不决,先看wiki
  2. 数学算法和程序实现还是有差别的
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 14,352评论 0 33
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,958评论 18 399
  • 总结一下常见的排序算法。 排序分内排序和外排序。内排序:指在排序期间数据对象全部存放在内存的排序。外排序:指在排序...
    jiangliang阅读 5,235评论 0 1
  • 首页 资讯 文章 资源 小组 相亲 登录 注册 首页 最新文章 IT 职场 前端 后端 移动端 数据库 运维 其他...
    Helen_Cat阅读 9,411评论 1 10
  • 医学证明,如果人体倾向酸性,体内细胞的作用就会变弱,废物就不易排出,肾脏、肝脏的负担就会加大,新陈代谢缓慢,各器官...
    刘祥168阅读 1,098评论 0 0