^異或實現兩數交換

轉載於:https://blog.csdn.net/zxm1306192988/article/details/50446399

原文:https://blog.csdn.net/u010141928/article/details/76140165 

通常我們實現兩數交換不得不引入一個臨時變量temp作爲媒介,而使用異或運算也能實現同樣的功能,甚至無需使用臨時變量。

這是一個通常的做法:

 


 

int main(){

int a=1,b=2,temp;

temp=a;

a=b;

b=temp;

printf("%d,%d\n",a,b);

return 0;

}


關於異或(Exclusive OR)

 

Wikipedia解釋

在數位邏輯中,邏輯算符互斥或閘exclusive or)是對兩個運算元的一種邏輯分析類型,符號爲XOREOR。與一般的或閘OR不同,當兩兩數值相同爲真..而有一數值不同時爲否..

兩個運算元(命題):A與B的異或一般寫成A異或B,或者寫成A \quad \mathrm{XOR} \quad BA \oplus BA \neq B等等。在C語言中,寫作A^B。

兩個相同數的異或結果爲0

異或真值表

 

A B A^B
0 0 0
0 1 1
1 0 1
1 1 0

 

異或的小例子

假設a爲二進制數01,b爲二進制數10,a^b的結果爲11並將其存儲在變量c中,經過反覆的測試,於是發現以下的規律:

 


 

11^01=10

11^10=01

 

 


 

c^a=b;

c^b=a;

可以很驚奇的發現,將兩數異或的結果與其中一數再進行異或,可以得到另一個數。
原理很簡單,兩數異或的結果保存了兩個數上每一個二進制位不同或相同的信息,如果相應的二進制位不同,就標誌爲1,如果相同,則標誌爲0。

 

由於任意一個二進制位與1異或有這樣一個特性:


 

0^1=1

1^1=0

即與1異或後,都將自己轉換成相反的位

 

 

這樣,我們就使用異或運算交換了兩數

   12(001100)

^ 34(100010)

-------------------

         101110

101110 ^ 001100=100010

101100 ^ 100010=001100

 


 

int main(){

    int a=12,b=34,temp;

    printf("Original result: a=%d,b=%d\n",a,b);

    temp=a^b;

    a=temp^a;

    b=temp^b;

    printf("Transformed result: a=%d,b=%d\n",a,b);

    return 0;

}

//////////////////////// result //////////////////////////
Original result: a=12,b=34

Transformed result: a=34,b=12

但是使用這種方法似乎與使用臨時變量沒有什麼區別?

其實不然,通過簡單分析可以發現臨時變量的值在整個過程中並沒有發生變化,因此也可以無需設置臨時變量。

 


 

a=a^b^a;

b=a^b^b;

於是可以使用第三種方法,將a設置爲臨時變量


 

a=a^b;

b=b^a;

a=b^a;

還可以寫得更簡潔一點:

a^=b^=a^=b;

 

還可以通過加減實現兩數互換:

a=a+b

b=a-b;

a=a-b;

前提是a+b的值不能溢出。

測試程序如下:

int main()
{
    int a = 4, b = 5;
    printf("a=%d b=%d\n", a, b);
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    printf("a=%d b=%d\n", a, b);
}

運行結果:
[root@test cs]# ./a.out
a=4 b=5
a=5 b=4
[ 注意:當a和b相等時,該方法不適用]

 

其他方法:

 


 

public void change1(int a, int b){

System.out.println(“change1交換之前\ta:”+a+”\tb:”+b);

a = a + b – (b = a);

System.out.println(“change1交換之後\ta:”+a+”\tb:”+b);

}


 

public void change2(int a, int b){//C/C++ 不適用

System.out.println(“change2交換之前\ta:”+a+”\tb:”+b);

b = a + (a = b)*0;

System.out.println(“change2交換之後\ta:”+a+”\tb:”+b);

}


 

public void change4(int a, int b){

System.out.println(“change4交換之前\ta:”+a+”\tb:”+b);

a = a * b;

b = a / b;

a = a / b;

System.out.println(“change4交換之後\ta:”+a+”\tb:”+b);

}

 

//---------------------------------------------------------------------------------

我們都知道可用通過異或運算交換兩個數,而不需要任何的中間變量。 如下面:

void exchange(int &a, int &b)
{
    a ^= b;
    b ^= a;
    a ^= b;
}

然而,這裏面卻存在着一個非常隱蔽的陷阱。

通常我們在對數組進行操作的時候,會交換數組中的兩個元素,如exchang(&a[i], &b[j]), 這兒如果i==j了(這種情況是很可能發生的),得到的結果就並非我們所期望的。

void main() 
{
   int a[2] = {1, 2};
   exchange(a[0], a[1]); //交換a[0]和a[1]的值
   printf("1---a[0]=%d a[1]=%d\n", a[0], a[1]);
   exchange(a[0], a[0]); //將a[0]與自己進行交換
   printf("2---a[0]=%d a[1]=%d\n", a[0], a[1]);
}
 上面那段測試代碼的輸出是:
1---a[0]=2 a[1]=1
2---a[0]=0 a[1]=1
很意外吧,第一次的交換正確的執行了,但是第二次調用exchange的時候卻將a[0]置爲了0. 仔細分析,不難發現,這正是我們在exchange裏面用異或實現交換所造成的。如果輸入a和b是同一個數,exchange裏面代碼相當於:

a ^= a;
a ^= a;
a ^= a;
成了a做了3次於自己的異或,其結果當然是0了。

既然這樣,我們就不能夠在任何使用交換的地方採用異或了,即使要用,也一定要在交換之前判斷兩個數是否已經相等了,如下:

    void exchange(int &a, int &b)
    {
        if(a == b) return; //防止&a,&b指向同一個地址;那樣結果會錯誤。
        a ^= b;
        b ^= a;
        a ^= b;
    }
--------------------- 



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章