在不使用第三个变量的情况下交换两个变量值

Muh*_*tar 97 c++

在采访中提出的一个非常棘手的问题.

交换两个变量的值,如a=10b=15.

通常要交换两个变量值,我们需要第三个变量,如:

temp=a;
a=b;
b=temp;
Run Code Online (Sandbox Code Playgroud)

现在的要求是,在不使用第三个变量的情况下交换两个变量的值.

Yac*_*oby 150

使用xor交换算法

void xorSwap (int* x, int* y) {
    if (x != y) { //ensure that memory locations are different
       *x ^= *y;
       *y ^= *x;
       *x ^= *y;
    }
}
Run Code Online (Sandbox Code Playgroud)


为什么要测试?

测试是为了确保x和y具有不同的内存位置(而不是不同的值).这是因为(p xor p) = 0如果x和y共享相同的内存位置,当一个设置为0时,两者都设置为0.当*x和*y都为0时,*x和*y上的所有其他xor操作将相等0(因为它们是相同的),这意味着该函数将*x和*y都设置为0.

如果它们具有相同的值但不是相同的内存位置,则一切都按预期工作

*x = 0011
*y = 0011
//Note, x and y do not share an address. x != y

*x = *x xor *y  //*x = 0011 xor 0011
//So *x is 0000

*y = *x xor *y  //*y = 0000 xor 0011
//So *y is 0011

*x = *x xor *y  //*x = 0000 xor 0011
//So *x is 0011
Run Code Online (Sandbox Code Playgroud)


应该使用吗?

一般情况下,没有.编译器将优化掉临时变量,并且假设交换是一个常见的过程,它应该为您的平台输出最佳的机器代码.

以这个用C编写的快速测试程序为例.

#include <stdlib.h>
#include <math.h>

#define USE_XOR 

void xorSwap(int* x, int *y){
    if ( x != y ){
        *x ^= *y;
        *y ^= *x;
        *x ^= *y;
    }
}

void tempSwap(int* x, int* y){
    int t;
    t = *y;
    *y = *x;
    *x = t;
}


int main(int argc, char* argv[]){
    int x = 4;
    int y = 5;
    int z = pow(2,28); 
    while ( z-- ){
#       ifdef USE_XOR
            xorSwap(&x,&y);
#       else
            tempSwap(&x, &y);
#       endif
    }
    return x + y;    
}
Run Code Online (Sandbox Code Playgroud)

编译使用:

gcc -Os main.c -o swap
Run Code Online (Sandbox Code Playgroud)

xor版本需要

real    0m2.068s
user    0m2.048s
sys  0m0.000s
Run Code Online (Sandbox Code Playgroud)

带临时变量的版本在哪里:

real    0m0.543s
user    0m0.540s
sys  0m0.000s
Run Code Online (Sandbox Code Playgroud)

  • @SoapBox好抓.修复并重新测试,我没有在时间上有任何重大差异. (4认同)
  • 提问者说两个变量,而不是整数.:P (3认同)

jk.*_*jk. 87

一般形式是:

A = A operation B
B = A inverse-operation B
A = A inverse-operation B 
Run Code Online (Sandbox Code Playgroud)

但是,您必须注意溢出,并且并非所有操作都具有针对定义操作的所有值明确定义的逆.例如*和/工作直到A或B为0

xor特别令人满意,因为它是针对所有整数定义的,并且是它自己的逆


Dor*_*Dor 80

a = a + b
b = a - b // b = a
a = a - b
Run Code Online (Sandbox Code Playgroud)

  • 如果'a + b`溢出怎么办? (16认同)
  • 在C中,在`unsigned`整数类型上使用它是可以的并且始终有效.在签名类型上,它将在溢出时调用未定义的行为. (10认同)
  • 如果a和b是相同的,基本的,大小的整数类型(如`int`,`unsigned short`,...),它仍然可以在最后工作,即使有溢出,因为如果a + b溢出,那么a - b将下溢.使用这些基本整数类型,值只是翻转. (3认同)
  • @Alok:没有考虑到这一点,因此是不切实际的:) (2认同)
  • @Shahbaz:即使有符号整数*存储*在 2 的补码中,溢出的行为仍然未定义。 (2认同)

CB *_*ley 77

没有人建议使用std::swap,但.

std::swap(a, b);
Run Code Online (Sandbox Code Playgroud)

不使用任何临时变量,并且取决于类型ab实现可能有一个也没有的specalization.应该在知道"技巧"是否合适的情况下编写实现.试图再次猜测是没有意义的.

更一般地说,我可能想要做这样的事情,因为它适用于类类型,使ADL能够在可能的情况下找到更好的重载.

using std::swap;
swap(a, b);
Run Code Online (Sandbox Code Playgroud)

当然,面试官对这个答案的反应可能会说明空缺.

  • 每个人都希望展示他们所学到的闪亮的xor或其他技巧,并提出有关如何使用它的各种警告,但这无疑是最好的答案. (15认同)
  • 为什么不std :: swap(a,b); ?我的意思是为什么使用? (2认同)
  • 没有用于用户定义类型的`std ::`,用户定义的`swap`实现的@Dinaiz也可以调用(这就是"它可以用于类类型,使ADL能够找到更好的重载,如果可能的话"). (2认同)

Vil*_*lx- 18

正如manu已经指出的那样,XOR算法是一种流行的算法,适用于所有整数值(包括指针,运气和铸造).为了完整起见,我想提一下另外一个不那么强大的加法/减法算法:

A = A + B
B = A - B
A = A - B
Run Code Online (Sandbox Code Playgroud)

在这里你必须小心溢出/下溢,但否则它的工作原理一样好.您甚至可以尝试使用浮点/双打,如果不允许XOR.


MSa*_*ers 10

愚蠢的问题应该得到适当的答案:

void sw2ap(int& a, int& b) {
  register int temp = a; // !
  a = b;
  b = temp;
}
Run Code Online (Sandbox Code Playgroud)

唯一很好用的register关键字.

  • 请注意,此答案适用于面试(呃),而非编制者.它特别利用了这样一个事实:那些提出这类问题的访问者并不真正理解C++.所以,他们不能拒绝这个答案.(并且没有标准答案; ISO C++谈论的是对象而不是变量). (6认同)