在采访中提到的一个非常棘手的问题。
交换两个变量的值,例如a=10
和b=15
。
一般来说要交换两个变量值,我们需要第三个变量,如:
temp=a;
a=b;
b=temp;
现在的要求是,在不使用第三个变量的情况下交换两个变量的值。
答案 0 :(得分:143)
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);
我不使用任何临时变量,并且根据a
和b
的类型,实现可能有一个也没有的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=10
,b=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=4
和b=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);
这很简单,但可能会引发警告。