인터뷰에서 묻는 매우 까다로운 질문 중 하나입니다.
같은 두 변수의 값을 교환 a=10하고b=15 .
일반적으로 두 변수 값을 바꾸려면 다음과 같은 세 번째 변수가 필요합니다.
temp=a;
a=b;
b=temp;
이제 요구 사항은 세 번째 변수를 사용하지 않고 두 변수의 값을 바꾸는 것입니다.
인터뷰에서 묻는 매우 까다로운 질문 중 하나입니다.
같은 두 변수의 값을 교환 a=10하고b=15 .
일반적으로 두 변수 값을 바꾸려면 다음과 같은 세 번째 변수가 필요합니다.
temp=a;
a=b;
b=temp;
이제 요구 사항은 세 번째 변수를 사용하지 않고 두 변수의 값을 바꾸는 것입니다.
답변:
은 Using XOR 스왑 알고리즘을
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) = 0x와 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
일반적인 형식은 다음과 같습니다.
A = A operation B
B = A inverse-operation B
A = A inverse-operation B
그러나 잠재적으로 오버플로를 조심해야하며 모든 작업에 작업이 정의 된 모든 값에 대해 잘 정의 된 역이있는 것은 아닙니다. 예 * 및 / A 또는 B가 0이 될 때까지 작동
xor는 모든 int에 대해 정의되고 자체 역이므로 특히 즐겁습니다.
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구현 에 따라 어느 쪽도 아닌 specalization을 가질 수 있습니다. 구현은 '트릭'이 적절한 지 여부를 알고 작성되어야합니다. 두 번째 추측을 시도하는 것은 의미가 없습니다.
일반적으로 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
여기서는 오버 플로우 / 언더 플로우에주의해야하지만 그렇지 않으면 잘 작동합니다. XOR이 허용되지 않는 경우 float / doubles에서 이것을 시도 할 수도 있습니다.
std::swap()입니까? 물론 포인터를 정수 로 변환 했다가 다시 되돌릴 수 있습니다 (보장되지 않는 충분히 큰 정수 유형이 있다고 가정). 그러나 그것은 당신이 제안한 것이 아닙니다. 포인터 가 정수로 변환 될 수있는 것이 아니라 정수라 는 것을 암시했습니다 . 예, 수락 된 답변은 포인터에 대해 작동하지 않습니다. Charles Bailey의 대답 사용 std::swap은 실제로 유일한 정답입니다.
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>표준이 아닙니다.
원래 솔루션은 다음과 같습니다.
temp = x; y = x; x = temp;
다음을 사용하여 2 개의 라이너로 만들 수 있습니다.
temp = x; y = y + temp -(x=y);
그런 다음 다음을 사용하여 하나의 라이너로 만듭니다.
x = x + y -(y=x);
y시퀀스 포인트없이 동일한 식으로 읽고 씁니다.
변수 대신 2 개의 어셈블리 레지스터에 대해 묻는 질문을 조금 변경하면 xchg작업을 하나의 옵션으로 사용하고 스택 작업을 다른 옵션으로 사용할 수 있습니다 .
xchg작업을 언급하고 있습니까? 질문은 CPU 아키텍처를 지정하지 않았습니다.
고려 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
a=10및 b=1.5.
#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);
}
세 번째 변수를 사용하지 않고 두 숫자를 바꾸는 간단한 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 (이진 1010) 및 5 (이진 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) 변수에 대한 포인터를 사용하고 함수 스왑을 만들 때 두 포인터가 동일한 변수를 가리킬 때 위의 모든 메서드가 실패합니다. 둘 다 동일한 변수를 가리키는 경우이 경우 어떤 일이 발생하는지 살펴 보겠습니다.
// 비트 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;
}
산출 :
스왑 후 (& 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
자바 스크립트에서 :
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
보시다시피 (그리고 많은 사람들이 말했듯이) 제자리에 스왑 (xor)은 임시 변수를 사용하는 다른 옵션보다 많은 시간이 걸립니다.
R에는 Edsger W. Dijkstra가 A Discipline of Programming , 1976, ch.4, p.29 에서 제안한 동시 할당이 누락되었습니다 . 이것은 우아한 솔루션을 허용합니다.
a, b <- b, a # swap
a, b, c <- c, a, b # rotate right
a = a + b - (b=a);
매우 간단하지만 경고가 발생할 수 있습니다.
b=a이 이전 또는 이후에 수행 되는지 여부 는 알 수 없습니다 a + b.
second_value -= first_value;
first_value += second_value;
second_value -= first_value;
second_value *= -1;