采访中提出的非常棘手的问题之一。
交换两个变量的值,例如a=10
和b=15
。
通常要交换两个变量值,我们需要第三个变量,例如:
temp=a;
a=b;
b=temp;
现在的要求是,不使用第三变量就交换两个变量的值。
采访中提出的非常棘手的问题之一。
交换两个变量的值,例如a=10
和b=15
。
通常要交换两个变量值,我们需要第三个变量,例如:
temp=a;
a=b;
b=temp;
现在的要求是,不使用第三变量就交换两个变量的值。
Answers:
使用异或交换算法
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共享相同的内存位置,则将1设置为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
a = a + b
b = a - b // b = a
a = a - b
a+b
溢出怎么办?
int
,unsigned short
...),即使在发生溢出的情况下,它最终仍会起作用,因为如果a + b溢出,则a-b将下溢。使用这些基本的整数类型,值仅会翻转。
unsigned
整数类型上使用此命令是可以的,并且始终有效。对于带符号的类型,它将在溢出时调用未定义的行为。
没有人建议使用std::swap
。
std::swap(a, b);
我不使用任何临时变量,具体取决于的类型a
和b
实现可能也没有具体化。应该在知道“技巧”是否合适的情况下编写实现。试图进行第二次猜测没有任何意义。
更笼统地说,我可能想做这样的事情,因为它将对使ADL尽可能找到更好的重载的类类型起作用。
using std::swap;
swap(a, b);
当然,访调员对此回答的反应可能说明了很多空缺。
std::
用户swap
定义类型的用户定义实现的@Dinaiz 也可以调用(这意味着“它将对类类型起作用,使ADL尽可能找到更好的重载”)。
std::swap
在实现中使用额外的临时内存吗?cplusplus.com/reference/utility/swap
正如manu已经指出的那样,XOR算法是一种流行的算法,适用于所有整数值(包括指针,然后包括运气和强制转换)。为了完整起见,我想提一下另一个功能较差的加/减算法:
A = A + B
B = A - B
A = A - B
在这里,您必须小心上溢/下溢,但否则它也可以正常工作。您甚至可以在floats / doubles上尝试这样做,以防在它们上不允许XOR。
std::swap
在不使用第三个变量的情况下实现。
愚蠢的问题应有适当的答案:
void sw2ap(int& a, int& b) {
register int temp = a; // !
a = b;
b = temp;
}
register
关键字的唯一好用法。
#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();
}
cout << "Enter the two no=:"
我期望阅读之后cout << "Now enter the two no in reverse order:"
a+b
或a-b
溢出,则具有未定义的行为。此外,它void main()
是无效的并且<conio.h>
是非标准的。
#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
这是另一种解决方案,但有一个风险。
码:
#include <iostream>
#include <conio.h>
void main()
{
int a =10 , b =45;
*(&a+1 ) = a;
a =b;
b =*(&a +1);
}
位置a + 1处的任何值都将被覆盖。
*(&a+1)
可能是b
。void main()
是无效的。<conio.h>
是非标准的(您甚至不使用它)。
那是正确的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
您可以...以简单的方式...在一行逻辑内
#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;
}
b
都是在同一表达式中读取和修改的,没有中间的序列点。
public void swapnumber(int a,int b){
a = a+b-(b=a);
System.out.println("a = "+a +" b= "+b);
}
最好的答案是使用XOR,并且在一行中使用它会很酷。
(x ^= y), (y ^= x), (x ^= y);
x,y是变量,并且它们之间的逗号引入了序列点,因此它不依赖于编译器。干杯!
让我们看一个简单的c示例,它不使用第三个变量就交换两个数字。
程序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返回一个数字,其中x和y的位不同时,所有位都为1。例如,10(在Binary 1010中)和5(在Binary 0101中)的XOR为1111,而7(0111)和5(0101)的XOR为(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)当我们使用指向变量的指针并进行函数交换时,当两个指针都指向同一个变量时,上述所有方法都会失败。让我们看看如果两种情况都指向同一个变量,将会发生什么情况。
//基于位异或的方法
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;
}
输出:
交换之后(&x,&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;
}
输出:
交换后(&x,&x):x = 10
在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变量的其他选项要花很多时间。
second_value -= first_value;
first_value += second_value;
second_value -= first_value;
second_value *= -1;