Trao đổi giá trị hai biến mà không sử dụng biến thứ ba


104

Một trong những câu hỏi rất khó trong một cuộc phỏng vấn.

Hoán đổi giá trị của hai biến như a=10b=15 .

Nói chung để hoán đổi giá trị hai biến, chúng ta cần biến thứ 3 như:

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

Bây giờ yêu cầu là hoán đổi giá trị của hai biến mà không sử dụng biến thứ 3.


76
Chà. Một câu hỏi phỏng vấn được IMHO lựa chọn kém. Đây là một kỹ thuật hiếm khi có ích trong thực tế. Có một cơ hội tốt là nó sẽ chỉ gây nhầm lẫn cho trình tối ưu hóa của trình biên dịch dẫn đến mã kém hiệu quả hơn là "hoán đổi tạm thời". Trừ khi nơi bạn đang phỏng vấn này liên quan đến những thứ rất nặng về toán học (hãy nghĩ: phát triển thuật toán mã hóa hoặc tương tự), tôi không thể tưởng tượng được bất kỳ lý do chính đáng nào để hỏi một câu hỏi như vậy.
Dan Molding

10
Thật vậy, câu hỏi đó không cho bạn biết bất cứ điều gì ngoài việc liệu ứng viên có biết thủ thuật cụ thể này rất vô dụng trong mã sản xuất hay không. Tôi cho rằng bạn có thể thỉnh thoảng gặp một thuật sĩ tìm ra nó ngay lập tức, nhưng những người như vậy mà không biết thủ thuật có thể là khá hiếm.
ceo

2
Có thể họ muốn loại bỏ những người nghĩ rằng biết những thủ thuật như vậy là điều tạo nên một lập trình viên giỏi? Ngoài ra, khi đọc về thủ thuật xor, hãy chú ý đến khi nào nó sẽ thất bại (IMO khiến nó hoàn toàn vô dụng đối với việc hoán đổi số nguyên có mục đích chung).
UncleBens

Câu trả lời:


155

Sử dụng thuật toán hoán đổi xor

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


Tại sao phải kiểm tra?

Kiểm tra là để đảm bảo rằng x và y có các vị trí bộ nhớ khác nhau (thay vì các giá trị khác nhau). Đây là bởi vì(p xor p) = 0 và nếu cả x và y chia sẻ cùng một vị trí bộ nhớ, khi một được đặt thành 0, cả hai đều được đặt thành 0. Khi cả * x và * y bằng 0, tất cả các phép toán xor khác trên * x và * y sẽ bằng nhau 0 (vì chúng giống nhau), có nghĩa là hàm sẽ đặt cả * x và * y được đặt thành 0.

Nếu chúng có cùng giá trị nhưng không cùng vị trí bộ nhớ, mọi thứ hoạt động như mong đợi

*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ái này có nên dùng không?

Trong các trường hợp chung, không. Trình biên dịch sẽ tối ưu hóa biến tạm thời và cho rằng hoán đổi là một thủ tục phổ biến, nó sẽ xuất ra mã máy tối ưu cho nền tảng của bạn.

Lấy ví dụ chương trình kiểm tra nhanh này được viết bằng 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;    
}

Biên dịch bằng cách sử dụng:

gcc -Os main.c -o swap

Phiên bản xor mất

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

Trong đó phiên bản có biến tạm thời chiếm:

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

1
Có thể có giá trị giải thích lý do tại sao kiểm tra (nghĩa là phương pháp ba xor thất bại khủng khiếp nếu x và y tham chiếu cùng một đối tượng).
dmckee --- cựu điều hành kitten

1
Tôi không biết làm thế nào mà điều này lại nhận được nhiều lượt ủng hộ khi mã bị hỏng như vậy. Cả hai hoán đổi trong đoạn mã được thử nghiệm đều hoàn toàn không chính xác. Nhìn gần.
SoapBox

@SoapBox: Điểm rất tốt! Các số 0 hoán đổi XOR x & để lại y nguyên và hoán đổi tạm thời để lại cả hai giá trị của x. Rất tiếc!
Hội trường Drew Hall

4
@SoapBox Bắt tốt. Đã sửa và kiểm tra lại và tôi không nhận được bất kỳ sự khác biệt lớn nào về thời gian.
Yacoby

4
Người hỏi nói hai biến, không phải int. : P
Plumenator 22/10/10

93

dạng chung là:

A = A operation B
B = A inverse-operation B
A = A inverse-operation B 

tuy nhiên, bạn phải đề phòng khả năng tràn và cũng không phải tất cả các phép toán đều có nghịch đảo được xác định rõ cho tất cả các giá trị mà phép toán đó được xác định. ví dụ: * và / hoạt động cho đến khi A hoặc B bằng 0

xor đặc biệt dễ chịu vì nó được định nghĩa cho tất cả các int và là nghịch đảo của chính nó


XOR (X, X) == 0, vì vậy xor hoạt động đối với số nguyên NGOẠI TRỪ khi hai giá trị được hoán đổi bằng nhau. Tôi không phải là người đầu tiên chỉ ra điều này và quá nhiều người đã nói điều đó (ở đây và ở nơi khác) để duy nhất bất cứ ai cho tín dụng.
AlanK

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

16
Nếu a+btràn thì sao?
Alok Singhal

2
@Alok: Điều đó không được xem xét, do đó nó không thực tế :)
Dor

4
Nếu a và b thuộc cùng kiểu số nguyên cơ bản, có kích thước giống nhau (như int,, unsigned short...), thì cuối cùng nó vẫn hoạt động, ngay cả khi bị tràn, bởi vì nếu a + b tràn thì a - b sẽ bị tràn. Với các kiểu số nguyên cơ bản này, các giá trị chỉ di chuyển qua.
Patrick Johnmeyer

10
Trong C, sử dụng điều này trên unsignedcác kiểu số nguyên là OK và luôn hoạt động. Trên các loại đã ký, nó sẽ gọi hành vi không xác định khi tràn.
jpalecek

1
@Shahbaz: Ngay cả khi các số nguyên có dấu được lưu trữ trong phần bổ sung của 2, hành vi khi tràn vẫn không được xác định.
Keith Thompson

79

Chưa có ai đề xuất sử dụng std::swap.

std::swap(a, b);

Tôi không sử dụng bất kỳ biến tạm thời nào và tùy thuộc vào loại ab việc triển khai có thể có một chỉ định cũng không. Việc triển khai phải được viết ra để biết liệu một 'thủ thuật' có phù hợp hay không. Không có ích gì khi cố gắng đoán lần thứ hai.

Nói chung hơn, tôi có thể muốn làm điều gì đó như thế này, vì nó sẽ hoạt động đối với các loại lớp cho phép ADL tìm quá tải tốt hơn nếu có thể.

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

Tất nhiên, phản ứng của người phỏng vấn đối với câu trả lời này có thể nói lên rất nhiều điều về vị trí tuyển dụng.


tất nhiên trong C ++ 0x swap sẽ sử dụng tham chiếu rvalue nên sẽ tốt hơn nữa!
jk.

16
Mọi người đều muốn thể hiện các xor sáng bóng hoặc thủ thuật khác mà họ đã học được, với nhiều cảnh báo khác nhau về cách sử dụng nó, nhưng chắc chắn đây là câu trả lời tốt nhất.

2
tại sao không std :: swap (a, b); ? Tôi có nghĩa là tại sao sử dụng?
Dinaiz

2
@Dinaiz không có std::, các swaptriển khai do người dùng xác định cho các loại do người dùng xác định cũng có sẵn để gọi (điều này có nghĩa là "nó sẽ hoạt động cho các loại lớp cho phép ADL tìm thấy quá tải tốt hơn nếu có thể").
Kyle Strand

std::swapsử dụng bộ nhớ tạm thời bổ sung trong việc thực hiện nó không? cplusplus.com/reference/utility/swap
Ninja

19

Như đã được lưu ý bởi manu, thuật toán XOR là một thuật toán phổ biến hoạt động cho tất cả các giá trị số nguyên (bao gồm cả con trỏ sau đó, với một số may mắn và ép kiểu). Để đầy đủ, tôi muốn đề cập đến một thuật toán khác kém mạnh mẽ hơn với phép cộng / trừ:

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

Ở đây bạn phải cẩn thận với các dòng tràn / tràn, nhưng nếu không thì nó vẫn hoạt động tốt. Bạn thậm chí có thể thử điều này trên phao / đôi trong trường hợp XOR không được phép trên chúng.


" tất cả các giá trị nguyên (bao gồm cả con trỏ sau đó) " - Cái gì? Không, con trỏ không phải là số nguyên và bạn không thể xor các giá trị của con trỏ. Bạn cũng không thể xor các giá trị dấu phẩy động. Nhưng phương pháp cộng / trừ có hành vi không xác định (đối với các loại dấu chấm động hoặc dấu phẩy động) trên phần tràn hoặc dòng dưới, có thể làm mất độ chính xác đối với dấu phẩy động và không thể áp dụng cho con trỏ hoặc các loại không phải số khác.
Keith Thompson

@KeithThompson - OK, việc mất độ chính xác đối với dấu chấm động là đúng, nhưng tùy thuộc vào trường hợp, nó có thể được chấp nhận. Đối với con trỏ - tốt, tôi chưa bao giờ nghe nói về một trình biên dịch / nền tảng nơi con trỏ không thể được truyền tự do sang số nguyên và quay lại một lần nữa (tất nhiên là hãy cẩn thận để chọn một số nguyên có kích thước phù hợp). Nhưng sau đó, tôi không phải là một chuyên gia C / C ++. Tôi biết nó đi ngược lại tiêu chuẩn, nhưng tôi có ấn tượng rằng hành vi này khá nhất quán trên ... mọi thứ.
Vilx-

1
@ Vilx-: Tại sao việc mất độ chính xác lại có thể chấp nhận được khi bạn có thể dễ dàng tránh nó bằng cách sử dụng một biến tạm thời - hoặc std::swap()? Chắc chắn, bạn có thể chuyển đổi con trỏ thành số nguyên và quay lại một lần nữa (giả sử có một kiểu số nguyên đủ lớn, điều này không được đảm bảo), nhưng đó không phải là những gì bạn đề xuất; bạn ngụ ý rằng con trỏ số nguyên, không phải là chúng có thể được chuyển đổi thành số nguyên. Có, câu trả lời được chấp nhận sẽ không hoạt động đối với con trỏ. Câu trả lời của Charles Bailey sử dụng std::swapthực sự là câu trả lời đúng duy nhất.
Keith Thompson

1
Đầu tiên, câu hỏi đặt ra là "làm thế nào để hoán đổi mà không sử dụng biến thứ ba" và lý do được đưa ra là "một câu hỏi phỏng vấn". Tôi chỉ đưa ra một câu trả lời khả dĩ khác. Thứ hai, OK, tôi xin lỗi vì ngụ ý rằng con trỏ là số nguyên. Tôi đã cập nhật câu trả lời để hy vọng rõ ràng và chính xác hơn. Vui lòng sửa cho tôi nếu tôi vẫn sai (hoặc tự cập nhật câu trả lời). Thứ ba, tôi đã xóa phần về câu trả lời được chấp nhận. Nó không sử dụng phôi con trỏ đến số nguyên ở bất kỳ đâu và nó hoạt động chính xác (theo như tôi hiểu).
Vilx-

@KeithThompson: Câu hỏi có thể được sửa đổi thành cách std::swapcó thể được triển khai mà không cần sử dụng biến thứ ba.
jxh

10

Những câu hỏi ngu ngốc xứng đáng có câu trả lời thích hợp:

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

Cách sử dụng tốt duy nhất của registertừ khóa.


1
Một đối tượng được khai báo với thanh ghi lớp lưu trữ không phải là một "biến"? Ngoài ra, tôi không tin rằng đây là một cách sử dụng tốt của đăng ký, vì nếu trình biên dịch của bạn không thể tối ưu hóa điều này rồi thì còn gì là cố gắng nữa, bạn nên chấp nhận bất cứ thứ gì rác rưởi bạn nhận được hoặc tự viết hợp ngữ ;-) Nhưng như bạn nói, một câu hỏi tinh quái xứng đáng có một câu trả lời tinh vi.
Steve Jessop

1
Khá nhiều trình biên dịch hiện đại bỏ qua lớp lưu trữ thanh ghi, vì họ có ý tưởng tốt hơn nhiều về những gì được truy cập thường xuyên hơn bạn.
ceo

6
Lưu ý rằng câu trả lời này dành cho (các) cuộc phỏng vấn, không phải cho người biên dịch. Nó đặc biệt lợi dụng thực tế là những người phỏng vấn hỏi loại câu hỏi này không thực sự hiểu C ++. Vì vậy, họ không thể từ chối câu trả lời này. (và không có câu trả lời Tiêu chuẩn; ISO C ++ nói về các đối tượng không phải là biến).
MSalters

À, tôi hiểu rồi. Tôi đã tìm kiếm trong tiêu chuẩn C để đề cập đến biến như một danh từ, và không chỉ có nghĩa là không phải const. Tôi đã tìm thấy một trong n1124, trong phần vòng lặp for xác định phạm vi của "biến" được khai báo trong trình khởi tạo. Sau đó, tôi nhận ra câu hỏi là C ++ và không bận tâm tìm kiếm để xem liệu C ++ có mắc lỗi đánh máy tương tự ở bất kỳ đâu.
Steve Jessop

3

Việc hoán đổi hai số bằng cách sử dụng biến thứ ba như thế này,

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

Trao đổi hai số mà không sử dụng biến thứ ba

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);

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();
}

1
Sau khi cout << "Enter the two no=:"tôi đã mong đợi để đọccout << "Now enter the two no in reverse order:"
user253751

Như với một số câu trả lời khác, điều này có hành vi không xác định nếu a+bhoặc a-btràn. Ngoài ra, void main()không hợp lệ và <conio.h>không chuẩn.
Keith Thompson

2

Vì giải pháp ban đầu là:

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

Bạn có thể làm cho nó thành hai lớp lót bằng cách sử dụng:

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

Sau đó, làm cho nó trở thành một lớp lót bằng cách sử dụng:

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

1
Hành vi không xác định. yđược đọc và viết trong cùng một biểu thức mà không có điểm trình tự xen vào.
Keith Thompson

1

Nếu bạn thay đổi một chút câu hỏi để hỏi về 2 thanh ghi hợp ngữ thay vì các biến, bạn cũng có thể sử dụng xchghoạt động như một tùy chọn và hoạt động ngăn xếp như một tùy chọn khác.


1
Bạn xchgđang đề cập đến hoạt động nào? Câu hỏi không chỉ định kiến ​​trúc CPU.
Keith Thompson

1

Xem xét a=10 , b=15:

Sử dụng phép cộng và phép trừ

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

Sử dụng phép chia và phép nhân

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

1
Có hành vi không xác định khi tràn.
Keith Thompson

1
Ngoài ra, trong C ++, nó có thể có hành vi không mong muốn khi các giá trị không cùng kiểu. Ví dụ, a=10b=1.5.
Matthew Cole

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

Cập nhật: Trong phần này, chúng tôi đang lấy đầu vào của hai số nguyên từ người dùng. Sau đó, chúng tôi đang sử dụng thao tác XOR bitwise để hoán đổi chúng.

Giả sử chúng ta có hai số nguyên a=4b=9và sau đó:

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

Vui lòng thêm giải thích ngắn gọn vào câu trả lời của bạn cho những khách truy cập trong tương lai.
Nikolay Mihaylov

1
Trong điều này, chúng tôi đang lấy đầu vào của hai số nguyên từ người dùng. Sau đó, chúng tôi đang sử dụng hoạt động bitwise xor hai hoán đổi chúng. Giả sử chúng ta có hai số nguyên a = 4 và b = 9 thì bây giờ a = a ^ b -> 13 = 4 ^ 9 b = a ^ b -> 4 = 13 ^ 9 a = a ^ b -> 9 = 13 ^ 9
Naeem Ul Hassan

1

Đây là một giải pháp nữa nhưng rủi ro duy nhất.

mã:

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

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

mọi giá trị tại vị trí a + 1 sẽ bị ghi đè.


2
Đó là một câu trả lời hữu ích, có lẽ giá trị đã biến mất.
Bibi Tahira

1
Một số loại hành vi không xác định. *(&a+1)cũng có thể là b. void main()không có hiệu lực. <conio.h>là không chuẩn (và bạn thậm chí không sử dụng nó).
Keith Thompson

Mã đã được thử nghiệm với các tham số đầu ra khác, đã loại trừ, như tôi đã đề cập đến một rủi ro. Nguy cơ bộ nhớ bị ghi đè .. Đó là đó .. Nhưng đó là một sự hoán đổi hữu ích hai biến mà không có sự tham gia của biến thứ ba.
Daredevil

Phần tồi tệ nhất là nó thực sự có thể làm việc một số lần, vì vậy bạn có thể không ngay lập tức nhận ra nó là hoàn toàn bị phá vỡ và sẽ thất bại dưới tối ưu hóa, trình biên dịch khác, vv
avl_sweden

1

Tất nhiên, câu trả lời C ++ phải là std::swap .

Tuy nhiên, cũng không có biến thứ ba trong việc triển khai sau swap:

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

Hoặc, dưới dạng một lớp lót:

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

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

0

đó là thuật toán hoán đổi XOR chính xác

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

bạn phải đảm bảo rằng vị trí bộ nhớ khác nhau và giá trị thực tế cũng khác vì A XOR A = 0


Bạn không cần phải đảm bảo các giá trị khác nhau.
user253751

0

Bạn có thể làm .... một cách dễ dàng ... trong một dòng Logic

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

hoặc là

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

1
Hành vi không xác định. Trong cả hai phiên bản, bđều được đọc và sửa đổi trong cùng một biểu thức, không có điểm trình tự can thiệp.
Keith Thompson

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

0

Câu trả lời tốt nhất là sử dụng XOR và sử dụng nó trong một dòng sẽ rất tuyệt.

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

x, y là các biến và dấu phẩy giữa chúng giới thiệu các điểm trình tự để nó không trở thành phụ thuộc trình biên dịch. Chúc mừng!


0

Hãy xem một ví dụ c đơn giản để hoán đổi hai số mà không sử dụng biến thứ ba.

chương trình 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();
}

Đầu ra:

Trước khi hoán đổi a = 10 b = 20 Sau khi hoán đổi a = 20 b = 10

Chương trình 2: Sử dụng * và /

Hãy xem một ví dụ khác để hoán đổi hai số bằng cách sử dụng * và /.

#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();
}

Đầu ra:

Trước khi hoán đổi a = 10 b = 20 Sau khi hoán đổi a = 20 b = 10

Chương trình 3: Sử dụng toán tử XOR bitwise:

Toán tử XOR bitwise có thể được sử dụng để hoán đổi hai biến. XOR của hai số x và y trả về một số có tất cả các bit là 1 bất cứ nơi nào các bit của x và y khác nhau. Ví dụ: XOR của 10 (Trong nhị phân 1010) và 5 (Trong nhị phân 0101) là 1111 và XOR của 7 (0111) và 5 (0101) là (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;

Đầu ra:

Sau khi hoán đổi: x = 5, y = 10

Chương trình 4:

Chưa có ai đề xuất sử dụng std :: swap.

std::swap(a, b);

Tôi không sử dụng bất kỳ biến tạm thời nào và tùy thuộc vào kiểu a và b, việc triển khai có thể có một chuyên môn hóa không. Việc triển khai phải được viết ra để biết liệu một 'thủ thuật' có phù hợp hay không.

Sự cố với các phương pháp trên:

1) Cách tiếp cận dựa trên phép nhân và phép chia sẽ không hoạt động nếu một trong các số là 0 vì tích trở thành 0 bất kể số còn lại.

2) Cả hai giải pháp Số học có thể gây ra tràn số học. Nếu x và y quá lớn, phép cộng và phép nhân có thể nằm ngoài phạm vi số nguyên.

3) Khi chúng ta sử dụng con trỏ đến một biến và thực hiện hoán đổi hàm, tất cả các phương pháp trên đều thất bại khi cả hai con trỏ đều trỏ đến cùng một biến. Hãy xem điều gì sẽ xảy ra trong trường hợp này nếu cả hai đều trỏ đến cùng một biến.

// Phương thức dựa trên Bitwise XOR

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

// Phương pháp dựa trên số học

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

Hãy cùng chúng tôi xem chương trình sau đây.

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

Đầu ra :

Sau khi hoán đổi (& x, & x): x = 0

Việc hoán đổi một biến với chính nó có thể cần thiết trong nhiều thuật toán tiêu chuẩn. Ví dụ, hãy xem triển khai QuickSort này, nơi chúng ta có thể hoán đổi một biến với chính nó. Vấn đề trên có thể tránh được bằng cách đặt một điều kiện trước khi hoán đổi.

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

Đầu ra :

Sau khi hoán đổi (& x, & x): x = 10


0

Có thể lạc đề, nhưng nếu bạn biết mình đang hoán đổi một biến đơn lẻ giữa hai giá trị khác nhau, bạn có thể thực hiện logic mảng. Mỗi khi dòng mã này được chạy, nó sẽ hoán đổi giá trị giữa 1 và 2.

n = [2, 1][n - 1]

0

Bạn có thể làm:

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

Hoặc sử dụng make_tuple khi hoán đổi nhiều hơn hai biến:

std::tie(x, y, z) = std::make_tuple(y, z, x);

Nhưng tôi không chắc liệu nội bộ std :: tie có sử dụng một biến tạm thời hay không!


0

Trong 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
}

Lưu ý đến thời gian thực hiện của cả hai tùy chọn.

Bằng cách chạy mã này, tôi đã đo được nó.

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

Như bạn có thể thấy (và như nhiều người đã nói), hoán đổi tại chỗ (xor) mất rất nhiều thời gian so với tùy chọn khác sử dụng biến tạm thời.


0

R thiếu một nhiệm vụ đồng thời theo đề xuất của Edsger W. Dijkstra trong A Discipline of Programming , 1976, ch.4, tr.29. Điều này sẽ cho phép một giải pháp thanh lịch:

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

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

Nó rất đơn giản, nhưng nó có thể đưa ra một cảnh báo.


5
Cảnh báo rằng nó không hoạt động? Chúng tôi không biết nếu b=ađược thực hiện trước hay sau a + b.
Bo Persson

-1

giải pháp một dòng để hoán đổi hai giá trị trong ngôn ngữ c.

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

Hành vi không xác định khi tràn.
Keith Thompson

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

Điều này có hành vi không xác định nếu bất kỳ hoạt động nào bị tràn hoặc tràn. Nó cũng có thể mất độ chính xác nếu các đối tượng là dấu phẩy động.
Keith Thompson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.