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

时间:2009-12-01 13:22:21

标签: c++

在采访中提到的一个非常棘手的问题。

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

一般来说要交换两个变量值,我们需要第三个变量,如:

temp=a;
a=b;
b=temp;

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

28 个答案:

答案 0 :(得分:143)

使用xor swap algorithm

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


为何进行测试?

测试是为了确保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


是否应该使用?

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

以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;    
}

使用编译:

gcc -Os main.c -o swap

xor版本需要

real    0m2.068s
user    0m2.048s
sys  0m0.000s

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

real    0m0.543s
user    0m0.540s
sys  0m0.000s

答案 1 :(得分:85)

一般形式是:

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

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

答案 2 :(得分:78)

a = a + b
b = a - b // b = a
a = a - b

答案 3 :(得分:73)

没有人建议使用std::swap

std::swap(a, b);

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

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

using std::swap;
swap(a, b);

当然,采访者对这个答案的反应可能会说明空缺。

答案 4 :(得分:17)

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

A = A + B
B = A - B
A = A - B

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

答案 5 :(得分:10)

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

void sw2ap(int& a, int& b) {
  register int temp = a; // !
  a = b;
  b = temp;
}

唯一可以很好地使用register关键字。

答案 6 :(得分:2)

这是另一个解决方案,但只有一个风险。

代码:

#include <iostream>
#include <conio.h>
void main()
{

int a =10 , b =45;
*(&a+1 ) = a;
a =b;
b =*(&a +1);
}

将覆盖位置a + 1处的任何值。

答案 7 :(得分:2)

由于最初的解决方案是:

temp = x; y = x; x = temp;

您可以使用以下方法将其制作成双色衬垫:

temp = x; y = y + temp -(x=y);

然后使用:

使其成为一个衬垫
x = x + y -(y=x);

答案 8 :(得分:2)

#include<iostream.h>
#include<conio.h>
void main()
{
int a,b;
clrscr();
cout<<"\n==========Vikas==========";
cout<<"\n\nEnter the two no=:";
cin>>a>>b;
cout<<"\na"<<a<<"\nb"<<b;
a=a+b;
b=a-b;
a=a-b;

cout<<"\n\na="<<a<<"\nb="<<b;
getch();
}

答案 9 :(得分:1)

如果你稍微改变一下问题就要问2个汇编寄存器而不是变量,你可以将xchg操作用作一个选项,将堆栈操作用作另一个选项。

答案 10 :(得分:1)

考虑a=10b=15

  

使用加法和减法

a = a + b //a=25
b = a - b //b=10
a = a - b //a=15
  

使用除法和乘法

a = a * b //a=150
b = a / b //b=10
a = a / b //a=15

答案 11 :(得分:1)

使用第三个变量交换两个数字就像这样,

int temp;
int a=10;
int b=20;
temp = a;
a = b;
b = temp;
printf ("Value of a", %a);
printf ("Value of b", %b);

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

int a = 10;
int b = 20;
a = a+b;
b = a-b;
a = a-b;
printf ("value of a=", %a);
printf ("value of b=", %b);

答案 12 :(得分:1)

#include <iostream>
using namespace std;
int main(void)
{   
 int a,b;
 cout<<"Enter a integer" <<endl;
 cin>>a;
 cout<<"\n Enter b integer"<<endl;
 cin>>b;

  a = a^b;
  b = a^b;
  a = a^b;

  cout<<" a= "<<a <<"   b="<<b<<endl;
  return 0;
}

更新:在此我们正在从用户输入两个整数。然后我们使用按位 XOR 操作来交换它们。

假设我们有两个整数a=4b=9,然后是:

a=a^b --> 13=4^9 
b=a^b --> 4=13^9 
a=a^b --> 9=13^9

答案 13 :(得分:1)

当然,C ++答案应该是std::swap

但是,swap的以下实现中也没有第三个变量:

template <typename T>
void swap (T &a, T &b) {
    std::pair<T &, T &>(a, b) = std::make_pair(b, a);
}

或者,作为一个单行:

std::make_pair(std::ref(a), std::ref(b)) = std::make_pair(b, a);

答案 14 :(得分:0)

R缺少Edsger W. Dijkstra在1976年的编程学科中提出的并发分配,第4章,第29页。这将提供一个优雅的解决方案:

a, b    <- b, a         # swap
a, b, c <- c, a, b      # rotate right

答案 15 :(得分:0)

怎么样,如果我们使用“GO”语言进行并行处理..

var x = 100; 变量 y = 200;

swap1 := func(var i) { x = i } swap2 := func(var y) { y = i } 并行化(swap1,swap2)

答案 16 :(得分:0)

在javascript中:

function swapInPlace(obj) {
    obj.x ^= obj.y
    obj.y ^= obj.x
    obj.x ^= obj.y
}

function swap(obj) {
    let temp = obj.x
    obj.x = obj.y
    obj.y = temp
}

请注意两个选项的执行时间。

通过运行此代码,我对其进行了测量。

console.time('swapInPlace')
swapInPlace({x:1, y:2})
console.timeEnd('swapInPlace') // swapInPlace: 0.056884765625ms

console.time('swap')
swap({x:3, y:6})
console.timeEnd('swap')        // swap: 0.01416015625ms

您可以看到(和很多人说的一样),就地交换(异或)比使用temp变量的其他选项要花很多时间。

答案 17 :(得分:0)

您可以这样做:

$ make
$ cat output.txt 
 _______ 
< hello >
 ------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

或在交换两个以上变量时使用make_tuple:

std::tie(x, y) = std::make_pair(y, x);

但是我不确定内部std :: tie是否使用临时变量!

答案 18 :(得分:0)

可能偏离主题,但如果您知道在两个不同值之间交换单个变量的内容,则可以执行数组逻辑。每次运行此代码行时,它将交换1到2之间的值。

    String query = "SELECT * FROM " + TABLE_INVOICE + " WHERE " + INVOICE_DATE + " / 86400000 = " + longDate + " / 86400000";

答案 19 :(得分:0)

最好的答案是使用XOR并在一行中使用它会很酷。

    (x ^= y), (y ^= x), (x ^= y);

x,y是变量,它们之间的逗号引入了序列点,因此它不会依赖于编译器。干杯!

答案 20 :(得分:0)

让我们看一个简单的例子,在不使用第三个变量的情况下交换两个数字。

计划1:

#include<stdio.h>
#include<conio.h>
main()
{
int a=10, b=20;
clrscr();
printf("Before swap a=%d b=%d",a,b);
a=a+b;//a=30 (10+20)
b=a-b;//b=10 (30-20)
a=a-b;//a=20 (30-10)
printf("\nAfter swap a=%d b=%d",a,b);
getch();
}

<强>输出:

交换之前a = 10 b = 20 交换后a = 20 b = 10

计划2:使用*和/

让我们看另一个使用*和/.

交换两个数字的例子
#include<stdio.h>
#include<conio.h>
main()
{
int a=10, b=20;
clrscr();
printf("Before swap a=%d b=%d",a,b);
a=a*b;//a=200 (10*20)
b=a/b;//b=10 (200/20)
a=a/b;//a=20 (200/10)
printf("\nAfter swap a=%d b=%d",a,b);
getch();
}

<强>输出:

交换之前a = 10 b = 20 交换后a = 20 b = 10

程序3:使用按位XOR运算符:

按位XOR运算符可用于交换两个变量。两个数字x和y的XOR返回一个数字,其中所有位都为1,只要x和y的位不同。例如,XOR为10(In Binary 1010)和5(In Binary 0101)为1111,XOR为7(0111)和5(0101)为(0010)。

#include <stdio.h>
int main()
{
 int x = 10, y = 5;
 // Code to swap 'x' (1010) and 'y' (0101)
 x = x ^ y;  // x now becomes 15 (1111)
 y = x ^ y;  // y becomes 10 (1010)
 x = x ^ y;  // x becomes 5 (0101)
 printf("After Swapping: x = %d, y = %d", x, y);
 return 0;

<强>输出:

交换后:x = 5,y = 10

计划4:

没有人建议使用std :: swap。

std::swap(a, b);

我不使用任何临时变量,并且根据a和b的类型,实现可能具有不同的特化。应该在知道“技巧”是否合适的情况下编写实现。

上述方法存在问题:

1)如果其中一个数字为0,那么基于乘法和除法的方法就不起作用,因为产品变为0而不管其他数字是什么。

2)算术解决方案都可能导致算术溢出。如果x和y太大,则加法和乘法可能会超出整数范围。

3)当我们使用指向变量的指针并进行函数交换时,当两个指针指向同一个变量时,上述所有方法都会失败。让我们来看看如果两者都指向同一个变量会发生什么。

//基于按位XOR的方法

x = x ^ x; // x becomes 0
x = x ^ x; // x remains 0
x = x ^ x; // x remains 0

//基于算术的方法

x = x + x; // x becomes 2x
x = x – x; // x becomes 0
x = x – x; // x remains 0

让我们看看以下计划。

#include <stdio.h>
void swap(int *xp, int *yp)
{
    *xp = *xp ^ *yp;
    *yp = *xp ^ *yp;
    *xp = *xp ^ *yp;
}

int main()
{
  int x = 10;
  swap(&x, &x);
  printf("After swap(&x, &x): x = %d", x);
  return 0;
}

<强>输出

交换后(&amp; x,&amp; x):x = 0

在许多标准算法中可能需要与自身交换变量。例如,请参阅QuickSort的这个实现,我们可以在其中交换变量。通过在交换之前设置条件可以避免上述问题。

#include <stdio.h>
void swap(int *xp, int *yp)
{
    if (xp == yp) // Check if the two addresses are same
      return;
    *xp = *xp + *yp;
    *yp = *xp - *yp;
    *xp = *xp - *yp;
}
int main()
{
  int x = 10;
  swap(&x, &x);
  printf("After swap(&x, &x): x = %d", x);
  return 0;
}

<强>输出

交换后(&amp; x,&amp; x):x = 10

答案 21 :(得分:0)

public void swapnumber(int a,int b){
    a = a+b-(b=a);
    System.out.println("a = "+a +" b= "+b);
}

答案 22 :(得分:0)

你可以......以简单的方式......在一行逻辑中

#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    int a = 1,b = 2;
    a=a^b^(b=a);
    printf("\nValue of A=%d B=%d ",a,b);

    return 1;
}

#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    int a = 1,b = 2;
    a=a+b-(b=a);
    printf("\nValue of A=%d B=%d ",a,b);

    return 1;
}

答案 23 :(得分:0)

这是正确的XOR交换算法

void xorSwap (int* x, int* y) {
   if (x != y) { //ensure that memory locations are different
      if (*x != *y) { //ensure that values are different
         *x ^= *y;
         *y ^= *x;
         *x ^= *y;
      }
   }
}

你必须确保内存位置不同,并且实际值也不同,因为A XOR A = 0

答案 24 :(得分:0)

#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter A :");
    scanf("%d",&a);
    printf("Enter B :");
    scanf("%d",&b);
    a ^= b;
    b ^= a;
    a ^= b;
    printf("\nValue of A=%d B=%d ",a,b);
    return 1;
}

答案 25 :(得分:-1)

second_value -= first_value;
first_value +=  second_value;
second_value -= first_value;
second_value *= -1;

答案 26 :(得分:-1)

用c语言交换两个值的单行解决方案。

a=(b=(a=a+b,a-b),a-b);

答案 27 :(得分:-1)

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

这很简单,但可能会引发警告。