การสลับค่าตัวแปรสองค่าโดยไม่ใช้ตัวแปรที่สาม


104

หนึ่งในคำถามที่ยุ่งยากมากที่ถามในการสัมภาษณ์

สลับค่าของตัวแปรทั้งสองชอบและa=10b=15

โดยทั่วไปในการสลับค่าตัวแปรสองค่าเราต้องการตัวแปรที่ 3 เช่น:

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

ตอนนี้ข้อกำหนดคือสลับค่าของสองตัวแปรโดยไม่ต้องใช้ตัวแปรที่ 3


76
ว้าว. คำถามสัมภาษณ์ IMHO ที่เลือกไม่ดี นี่เป็นเทคนิคที่ไม่ค่อยมีประโยชน์ในทางปฏิบัติ มีโอกาสที่ดีที่จะทำให้เครื่องมือเพิ่มประสิทธิภาพของคอมไพเลอร์สับสนเท่านั้นซึ่งส่งผลให้โค้ดมีประสิทธิภาพน้อยกว่า "การสลับชั่วคราว" เว้นแต่สถานที่ที่คุณกำลังสัมภาษณ์อยู่นี้มีส่วนเกี่ยวข้องกับสิ่งที่หนักมากทางคณิตศาสตร์ (คิดว่า: การพัฒนาอัลกอริธึมการเข้ารหัสหรืออื่น ๆ ) ฉันนึกไม่ออกว่าจะมีเหตุผลที่ดีที่จะถามคำถามเช่นนี้
แดนปั้น

10
อันที่จริงคำถามนั้นไม่ได้บอกอะไรคุณนอกจากว่าผู้สมัครรู้เคล็ดลับเฉพาะนี้ซึ่งค่อนข้างไร้ประโยชน์ในรหัสการผลิต ฉันคิดว่าคุณอาจวิ่งข้ามตัวช่วยสร้างบางครั้งที่คิดออกได้ทันที แต่คนแบบนั้นที่ไม่รู้เคล็ดลับนั้นน่าจะหายาก
ceo

2
อาจเป็นเพราะพวกเขาต้องการกำจัดคนที่คิดว่าการรู้เทคนิคดังกล่าวเป็นสิ่งที่ทำให้โปรแกรมเมอร์ที่ดี? นอกจากนี้เมื่ออ่านเกี่ยวกับ xor-trick ให้ใส่ใจว่าเมื่อใดจะล้มเหลว (ซึ่ง IMO ทำให้การแลกเปลี่ยนจำนวนเต็มโดยทั่วไปนั้นไร้ประโยชน์อย่างสิ้นเชิง)
UncleBens

คำตอบ:


155

ใช้อัลกอริทึมการแลกเปลี่ยน 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) = 0และถ้าทั้ง x และ y ใช้ตำแหน่งหน่วยความจำเดียวกันเมื่อตั้งค่าเป็น 0 ทั้งสองจะถูกตั้งค่าเป็น 0 เมื่อทั้ง * x และ * y เป็น 0 การดำเนินการ xor อื่น ๆ ทั้งหมดบน * x และ * y จะเท่ากัน 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
อาจจะคุ้มค่าที่จะอธิบายถึงสาเหตุของการทดสอบ (กล่าวคือวิธี triple xor ล้มเหลวอย่างน่ากลัวหาก x และ y อ้างอิงวัตถุเดียวกัน)
dmckee --- อดีตผู้ดูแลลูกแมว

1
ฉันไม่รู้ว่ามันมีการโหวตเพิ่มขึ้นมากมายขนาดนี้ได้อย่างไรเมื่อโค้ดเสียมาก การแลกเปลี่ยนทั้งสองในส่วนรหัสที่ทดสอบไม่ถูกต้องทั้งหมด ดูใกล้ ๆ
SoapBox

@SoapBox: จุดดีมาก! XOR swap zeros x & ปล่อย y โดยไม่ถูกแตะต้องและ temp swap จะปล่อยให้ทั้งคู่มีค่า x อ๊ะ!
ดรูว์ฮอลล์

4
@SoapBox จับดี. แก้ไขและทดสอบใหม่แล้วและฉันไม่ได้รับความแตกต่างที่สำคัญในการกำหนดเวลา
Yacoby

4
ผู้ถามกล่าวว่าตัวแปรสองตัวไม่ใช่ ints : P
Plumenator

93

รูปแบบทั่วไปคือ:

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

อย่างไรก็ตามคุณอาจต้องระวังการล้นและไม่ใช่การดำเนินการทั้งหมดที่มีการผกผันที่กำหนดไว้อย่างดีสำหรับค่าทั้งหมดที่มีการกำหนดการดำเนินการ เช่น * และ / ทำงานจนกว่า A หรือ B จะเป็น 0

xor เป็นที่ชื่นชอบเป็นพิเศษเนื่องจากมีการกำหนดไว้สำหรับ ints ทั้งหมดและเป็นค่าผกผัน


XOR (X, X) == 0 ดังนั้น xor จึงใช้ได้กับ ints EXCEPT เมื่อค่าสองค่าที่สลับกันเท่ากัน ฉันไม่ใช่คนแรกที่ชี้ให้เห็นเรื่องนี้และมีหลายคนพูดถึงเรื่องนี้ (ที่นี่และที่อื่น ๆ ) เพื่อให้ทุกคนได้รับเครดิต
AlanK

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

16
เกิดอะไรขึ้นถ้าa+bล้น?
Alok Singhal

2
@Alok: นั่นไม่ได้ถูกนำมาพิจารณาจึงเป็นไปไม่ได้ :)

4
หาก a และ b เป็นประเภทจำนวนเต็มพื้นฐานที่มีขนาดเท่ากัน (เช่นint, unsigned short... ) ก็ยังคงใช้งานได้ในท้ายที่สุดแม้จะล้นเพราะถ้า a + b ล้นออกมา a - b ก็จะน้อยเกินไป ด้วยประเภทจำนวนเต็มพื้นฐานเหล่านี้ค่าจะเป็นแบบโรลโอเวอร์
Patrick Johnmeyer

11
ใน C การใช้สิ่งนี้กับunsignedประเภทจำนวนเต็มนั้นใช้ได้และใช้ได้เสมอ ในประเภทที่มีการลงนามมันจะเรียกใช้พฤติกรรมที่ไม่ได้กำหนดบนโอเวอร์โฟลว์
jpalecek

2
@ Shahbaz: แม้ว่าจำนวนเต็มที่ลงนามจะถูกเก็บไว้ในส่วนเติมเต็ม 2 แต่พฤติกรรมของโอเวอร์โฟลว์ก็ยังไม่ได้กำหนด
Keith Thompson

79

ไม่มีใครได้แนะนำให้ใช้std::swapแต่

std::swap(a, b);

ฉันไม่ได้ใช้ตัวแปรชั่วคราวใด ๆ และขึ้นอยู่กับประเภทaและbการนำไปใช้งานอาจมี specalization ที่ไม่มีเช่นกัน การดำเนินการควรเขียนโดยรู้ว่า 'เคล็ดลับ' เหมาะสมหรือไม่ ไม่มีจุดที่จะพยายามเดาครั้งที่สอง

โดยทั่วไปแล้วฉันอาจต้องการทำสิ่งนี้เนื่องจากจะใช้ได้กับประเภทคลาสที่ทำให้ ADL สามารถหาโอเวอร์โหลดที่ดีกว่าได้ถ้าเป็นไปได้

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

แน่นอนว่าปฏิกิริยาของผู้สัมภาษณ์ต่อคำตอบนี้อาจพูดได้มากเกี่ยวกับตำแหน่งที่ว่าง


แน่นอนในการแลกเปลี่ยน C ++ 0x จะใช้การอ้างอิง rvalue ดังนั้นจะดียิ่งขึ้น!
jk.

16
ทุกคนต้องการอวด xor ที่แวววาวหรือกลเม็ดอื่น ๆ ที่พวกเขาได้เรียนรู้โดยมีข้อแม้ต่าง ๆ เกี่ยวกับวิธีการใช้งาน แต่สิ่งนี้เป็นคำตอบที่ดีที่สุด

2
ทำไมไม่ std :: swap (a, b); เหรอ? ฉันหมายถึงทำไมใช้?
Dinaiz

2
@Dinaiz ที่ไม่มีการstd::ใช้งานที่กำหนดโดยswapผู้ใช้สำหรับประเภทที่ผู้ใช้กำหนดเองก็มีให้เรียกใช้เช่นกัน (นี่คือความหมายของ "มันจะใช้ได้กับประเภทคลาสที่ทำให้ ADL สามารถหาโอเวอร์โหลดที่ดีกว่าได้ถ้าเป็นไปได้")
Kyle Strand

std::swapใช้หน่วยความจำชั่วคราวเพิ่มเติมในการใช้งานไม่ใช่หรือ cplusplus.com/reference/utility/swap
Ninja

19

ตามที่ระบุไว้แล้วโดย manu อัลกอริทึม XOR เป็นวิธีที่ได้รับความนิยมซึ่งใช้ได้กับค่าจำนวนเต็มทั้งหมด (ซึ่งรวมถึงตัวชี้ด้วยโชคและการร่าย) เพื่อความสมบูรณ์ฉันอยากจะพูดถึงอัลกอริทึมที่มีประสิทธิภาพน้อยกว่าอีกอย่างหนึ่งโดยมีการบวก / ลบ:

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

ที่นี่คุณต้องระวังน้ำล้น / น้ำล้น แต่อย่างอื่นก็ใช้ได้ดีเหมือนกัน คุณอาจลองใช้การลอยตัว / คู่ในกรณีที่ XOR ไม่ได้รับอนุญาต


" ค่าจำนวนเต็มทั้งหมด (ซึ่งรวมถึงพอยน์เตอร์แล้ว) " - อะไร? ไม่พอยน์เตอร์ไม่ใช่จำนวนเต็มและคุณไม่สามารถ x หรือค่าตัวชี้ได้ คุณยัง x หรือค่าทศนิยมไม่ได้ แต่วิธีการบวก / ลบมีลักษณะการทำงานที่ไม่ได้กำหนดไว้ (สำหรับประเภทจุดที่มีการลงนามหรือลอยตัว) ในการล้นหรือขั้นต่ำอาจสูญเสียความแม่นยำสำหรับจุดลอยตัวและไม่สามารถใช้กับตัวชี้หรือประเภทอื่นที่ไม่ใช่ตัวเลขได้
Keith Thompson

@KeithThompson - โอเคการสูญเสียความแม่นยำสำหรับจุดลอยตัวนั้นเป็นเรื่องจริง แต่ขึ้นอยู่กับสถานการณ์ก็สามารถยอมรับได้ สำหรับคำแนะนำ - ฉันไม่เคยได้ยินเกี่ยวกับคอมไพเลอร์ / แพลตฟอร์มที่พอยน์เตอร์ไม่สามารถส่งเป็นจำนวนเต็มได้อย่างอิสระและกลับมาอีกครั้ง (แน่นอนว่าต้องเลือกจำนวนเต็มในขนาดที่เหมาะสมด้วย) แต่ฉันไม่ใช่ผู้เชี่ยวชาญ C / C ++ ฉันรู้ว่ามันขัดต่อมาตรฐาน แต่ฉันรู้สึกว่าพฤติกรรมนี้ค่อนข้างสอดคล้องกันใน ...
Vilx-

1
@ Vilx-: เหตุใดการสูญเสียความแม่นยำจึงเป็นที่ยอมรับในเมื่อมันง่ายมากที่จะหลีกเลี่ยงโดยใช้ตัวแปรชั่วคราว - หรือstd::swap()? แน่นอนว่าคุณสามารถแปลงพอยน์เตอร์เป็นจำนวนเต็มและย้อนกลับได้อีกครั้ง (สมมติว่ามีจำนวนเต็มมากพอซึ่งไม่รับประกัน) แต่นั่นไม่ใช่สิ่งที่คุณแนะนำ คุณบอกเป็นนัยว่าพอยน์เตอร์เป็นจำนวนเต็มไม่ใช่ว่าสามารถแปลงเป็นจำนวนเต็มได้ ใช่คำตอบที่ยอมรับจะใช้กับพอยน์เตอร์ไม่ได้ คำตอบที่ชาร์ลส์เบลีย์ใช้std::swapเป็นจริงที่ถูกต้องเพียงอย่างใดอย่างหนึ่ง
Keith Thompson

1
ประการแรกคำถามคือ "วิธีการสลับโดยไม่ใช้ตัวแปรที่สาม" และเหตุผลที่ให้คือ "คำถามสัมภาษณ์" ฉันแค่ให้คำตอบอื่นที่เป็นไปได้ อย่างที่สองโอเคขอโทษที่บอกเป็นนัยว่าพอยน์เตอร์เป็นจำนวนเต็ม ฉันอัปเดตคำตอบเพื่อให้ชัดเจนและถูกต้องยิ่งขึ้น โปรดแก้ไขฉันหากฉันยังคงผิดพลาด (หรืออัปเดตคำตอบเอง) ประการที่สามฉันลบส่วนที่เกี่ยวกับคำตอบที่ยอมรับ มันไม่ได้ใช้ตัวชี้ถึงจำนวนเต็มที่ใดก็ได้และทำงานได้อย่างถูกต้อง (เท่าที่ฉันเข้าใจ)
Vilx-

@KeithThompson: คำถามสามารถแก้ไขได้เกี่ยวกับวิธีstd::swapการนำไปใช้โดยไม่ใช้ตัวแปรที่สาม
jxh

10

คำถามโง่ ๆ สมควรได้รับคำตอบที่เหมาะสม:

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

การใช้registerคำหลักที่ดีเท่านั้น


1
ออบเจ็กต์ที่ประกาศด้วยรีจิสเตอร์คลาสสตอเรจไม่ใช่ "ตัวแปร" หรือไม่? นอกจากนี้ฉันไม่มั่นใจว่านี่เป็นการใช้รีจิสเตอร์ที่ดีเนื่องจากหากคอมไพเลอร์ของคุณไม่สามารถปรับให้เหมาะสมได้แล้วสิ่งที่ต้องลองคืออะไรคุณควรยอมรับขยะที่คุณได้รับหรือเขียนแอสเซมบลีด้วยตัวเอง ;-) แต่อย่างที่ คุณพูดว่าคำถามที่หลบหลีกสมควรได้รับคำตอบแบบหลบ ๆ
Steve Jessop

1
คอมไพเลอร์สมัยใหม่เกือบทั้งหมดไม่สนใจคลาสพื้นที่จัดเก็บรีจิสเตอร์เนื่องจากพวกเขามีความคิดที่ดีกว่าว่ามีการเข้าถึงอะไรบ่อยกว่าที่คุณทำ
ceo

6
โปรดทราบว่าคำตอบนี้มีไว้สำหรับการสัมภาษณ์ไม่ใช่คอมไพเลอร์ โดยเฉพาะอย่างยิ่งใช้ประโยชน์จากข้อเท็จจริงที่ว่าผู้สัมภาษณ์ที่ถามคำถามประเภทนี้ไม่เข้าใจ C ++ อย่างแท้จริง ดังนั้นพวกเขาจึงไม่สามารถปฏิเสธคำตอบนี้ได้ (และไม่มีคำตอบมาตรฐาน ISO C ++ พูดถึงวัตถุไม่ใช่ตัวแปร)
MSalters

ฉันเข้าใจคุณ ฉันกำลังมองหาในมาตรฐาน C สำหรับการพูดถึงตัวแปรเป็นคำนามและไม่ใช่แค่ความหมายที่ไม่ใช่ const ฉันพบหนึ่งใน n1124 ในส่วนของลูปที่กำหนดขอบเขตของ "ตัวแปร" ที่ประกาศในตัวเริ่มต้น จากนั้นฉันก็รู้ว่าคำถามคือ C ++ และไม่ต้องกังวลกับการค้นหาเพื่อดูว่า C ++ พิมพ์ผิดที่ใดหรือไม่
Steve Jessop

3

การสลับตัวเลขสองตัวโดยใช้ตัวแปรที่สามจะเป็นเช่นนี้

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

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
หลังจากที่cout << "Enter the two no=:"ฉันคาดหวังว่าจะได้อ่านcout << "Now enter the two no in reverse order:"
user253751

เช่นเดียวกับคำตอบอื่น ๆ สิ่งนี้มีพฤติกรรมที่ไม่ได้กำหนดหากa+bหรือa-bมากเกินไป นอกจากนี้ยังvoid main()ไม่ถูกต้องและ<conio.h>ไม่ได้มาตรฐาน
Keith Thompson

2

เนื่องจากโซลูชันดั้งเดิมคือ:

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

คุณสามารถทำให้มันเป็นสองซับโดยใช้:

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

จากนั้นทำให้เป็นซับเดียวโดยใช้:

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

1
พฤติกรรมที่ไม่ได้กำหนด yถูกอ่านและเขียนด้วยนิพจน์เดียวกันโดยไม่มีจุดลำดับการแทรกแซง
Keith Thompson

1

หากคุณเปลี่ยนคำถามเล็กน้อยเพื่อถามเกี่ยวกับการลงทะเบียนแอสเซมบลี 2 ตัวแทนที่จะเป็นตัวแปรคุณสามารถใช้การxchgดำเนินการเป็นตัวเลือกหนึ่งและการดำเนินการสแต็กเป็นอีกตัวหนึ่งได้


1
สิ่งที่xchgคุณกำลังดำเนินการหมายถึง? คำถามไม่ได้ระบุสถาปัตยกรรมของ CPU
Keith Thompson

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

1
มีพฤติกรรมที่ไม่ได้กำหนดเกี่ยวกับการล้น
Keith Thompson

1
นอกจากนี้ใน C ++ อาจมีพฤติกรรมที่ไม่พึงประสงค์เมื่อค่าไม่ใช่ประเภทเดียวกัน ตัวอย่างเช่นa=10และb=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;
}

อัปเดต:ในสิ่งนี้เรากำลังรับข้อมูลจำนวนเต็มสองจำนวนจากผู้ใช้ จากนั้นเรากำลังใช้การดำเนินการXOR แบบบิตเพื่อสลับมัน

บอกว่าเรามีสองจำนวนเต็มa=4และb=9แล้ว:

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

โปรดเพิ่มคำอธิบายสั้น ๆ ในคำตอบของคุณสำหรับผู้เยี่ยมชมในอนาคต
Nikolay Mihaylov

1
ในสิ่งนี้เรากำลังรับข้อมูลจำนวนเต็มสองจำนวนจากผู้ใช้ จากนั้นเรากำลังใช้การดำเนินการ bitwise xor สองสลับกัน สมมติว่าเรามีสอง intergers a = 4 และ b = 9 แล้วตอนนี้ a = a ^ b -> 13 = 4 ^ 9 b = a ^ b -> 4 = 13 ^ 9 a = a ^ b -> 9 = 13 ^ 9
Naeem Ul Hassan

1

นี่เป็นอีกหนึ่งวิธีแก้ปัญหา แต่มีความเสี่ยงเดียว

รหัส:

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

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

ค่าใด ๆ ในสถานที่ที่ +1 จะถูกลบล้าง


2
มันเป็นคำตอบที่มีประโยชน์บางทีคุณค่าอาจหายไป
Bibi Tahira

1
พฤติกรรมที่ไม่ได้กำหนดหลายประเภท อาจจะดี*(&a+1) ไม่ถูกต้อง ไม่ได้มาตรฐาน (และคุณไม่ได้ใช้ด้วยซ้ำ) bvoid main()<conio.h>
Keith Thompson

รหัสได้รับการทดสอบกับพารามิเตอร์เอาต์พุตอื่น ๆ ซึ่งไม่รวมอยู่ด้วยเนื่องจากฉันได้กล่าวถึงความเสี่ยง ความเสี่ยงที่หน่วยความจำจะถูกลบล้าง .. นั่นคือที่นั่น .. แต่มันเป็นการแลกเปลี่ยนสองตัวแปรที่มีประโยชน์โดยไม่ต้องเกี่ยวข้องกับสาม
DareDevil

ส่วนที่แย่ที่สุดคือมันอาจใช้งานได้จริงในบางครั้งดังนั้นคุณอาจไม่รู้ในทันทีว่ามันเสียอย่างสมบูรณ์และจะล้มเหลวภายใต้การเพิ่มประสิทธิภาพคอมไพเลอร์อื่น ๆ ฯลฯ
avl_sweden

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

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

นั่นคืออัลกอริทึมการแลกเปลี่ยน 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


คุณไม่จำเป็นต้องแน่ใจว่าค่าต่างกัน
user253751

0

คุณสามารถทำได้ .... ด้วยวิธีง่ายๆ ... ภายในบรรทัดเดียว 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;
}

หรือ

#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
พฤติกรรมที่ไม่ได้กำหนด ในทั้งสองเวอร์ชันbมีทั้งอ่านและแก้ไขภายในนิพจน์เดียวกันโดยไม่มีจุดลำดับการแทรกแซง
Keith Thompson


0

คำตอบที่ดีที่สุดคือการใช้ XOR และการใช้งานในบรรทัดเดียวจะดีกว่า

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

x, y เป็นตัวแปรและเครื่องหมายจุลภาคระหว่างทั้งสองจะแนะนำจุดลำดับดังนั้นจึงไม่ขึ้นอยู่กับคอมไพเลอร์ ไชโย!


0

มาดูตัวอย่าง 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 แบบบิตสามารถใช้เพื่อสลับสองตัวแปร XOR ของตัวเลขสองตัว x และ y จะส่งกลับตัวเลขที่มีบิตทั้งหมดเป็น 1 โดยที่บิตของ x และ y ต่างกัน ตัวอย่างเช่น XOR ของ 10 (ใน Binary 1010) และ 5 (ใน 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) เมื่อเราใช้พอยน์เตอร์กับตัวแปรและทำการสลับฟังก์ชันวิธีการทั้งหมดข้างต้นจะล้มเหลวเมื่อพอยน์เตอร์ทั้งสองชี้ไปที่ตัวแปรเดียวกัน ลองมาดูว่าจะเกิดอะไรขึ้นในกรณีนี้หากทั้งสองชี้ไปที่ตัวแปรเดียวกัน

// วิธีการใช้ Bitwise 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


0

อาจจะไม่ตรงประเด็น แต่ถ้าคุณรู้ว่าคุณกำลังสลับตัวแปรเดียวระหว่างค่าที่ต่างกันสองค่าคุณอาจสามารถใช้ตรรกะของอาร์เรย์ได้ ทุกครั้งที่เรียกใช้โค้ดบรรทัดนี้โค้ดจะสลับค่าระหว่าง 1 ถึง 2

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

0

คุณสามารถทำได้:

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

หรือใช้ make_tuple เมื่อสลับตัวแปรมากกว่าสองตัวแปร:

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

แต่ฉันไม่แน่ใจว่าภายใน std :: tie ใช้ตัวแปรชั่วคราวหรือไม่!


0

ในจาวาสคริปต์:

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) ใช้เวลานานกว่าตัวเลือกอื่น ๆ โดยใช้ตัวแปร temp


0

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

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

มันง่ายมาก แต่อาจมีคำเตือน


5
คำเตือนว่ามันไม่ได้ผล? เราไม่ทราบว่าจะดำเนินการก่อนหรือหลังb=a a + b
Bo Persson

-1

โซลูชันบรรทัดเดียวสำหรับการแลกเปลี่ยนสองค่าในภาษา c

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

พฤติกรรมที่ไม่ได้กำหนดเมื่อล้น
Keith Thompson

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

สิ่งนี้มีลักษณะการทำงานที่ไม่ได้กำหนดหากการดำเนินการใด ๆ ล้นหรือน้อยเกินไป นอกจากนี้ยังอาจสูญเสียความแม่นยำหากวัตถุเป็นจุดลอยตัว
Keith Thompson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.