巧用“異或”


  “異或”運算是C語言中一種比較冷僻的運算,除了一些特定領域的問題(如加密、圖像處理等),較少有恰當的應用場合。以至於大多數C語言書在講到異或這個運算時,一般都干巴巴的很生硬。

  日前,看到 人人校招筆試題  中的對某問題的求解,發現異或在某些特定場合有非常奇妙的、恰如其分的甚至可說是舍我其誰的應用。

  人人校招筆試題 中的問題是這樣的:

  給定一個有序數組a,長度為len,和一個數x,判斷A數組里面是否存在兩個數,他們的和為x,bool judge(int *a, int len, int x),存在返回true,不存在則返回false。

  這個問題並不太難,除了博主給出了代碼,另有網友給出了另外兩種代碼。

代碼1 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int Judge(int *a, int len, int x)
{
    int Ascending = 0;//為1表示升序,否則降序
    Ascending = a[1] > a[0] ? 1 : 0;
    int *CopyA = (int *)malloc(sizeof(int) * len);
    memset(CopyA, 0, sizeof(int) * len);
    //構建另一數組
    int icount = 0;
    for(icount = 0; icount < len; icount++)
    {
        CopyA[icount] = x - a[icount];
    }
    //比較兩個指針移動的值,這里用索引代替指針
    int i = len - 1, j = 0;
    while(i >= 0 && j < len)
    {
        if(a[i] > CopyA[j])
        {
            switch(Ascending)
            {
                case 0: j++; break;//降序
                case 1: i--; break;//升序
                default:break;
            }
        }
        else if(a[i] == CopyA[j])
        {
            return 1;
        }
        else
        {
            switch(Ascending)
            {
                case 0: i--; break;//降序
                case 1: j++; break;//升序
                default:break;
            }
        }
    }
    return 0;
}
int main()
{
    int a[] = {59,41,21,10,5};
    int len = 5;
    int x = 190;
    switch(Judge(a, len, x))
    {
        case 0: printf("%d isn't exist!", x);break;
        case 1: printf("%d is exist!", x);break;
        default : break;
    }
    return 0;
}
View Code

 代碼2

bool judge(int *a, int len, int x) 
{ 
    int begin = 0; 
    int end = len - 1; 
    int order = 0;       /*0-升序 1-降序*/
      
    /*判斷升序還是降序*/
    if(a[0] < a[len - 1]) 
    { 
        order = 0; 
    } 
    else
    { 
        order = 1; 
    } 
      
    while(begin < end) 
    { 
        if(a[begin] + a[end] > x) 
        { 
            if(order == 0) 
                --end; 
            else
                ++begin; 
        } 
        else if(a[begin] + a[end] == x) 
        { 
            return true; 
        } 
        else
        { 
            if(order == 0) 
                ++begin; 
            else
                --end; 
        } 
    } 
  
    return false; 
}
View Code

代碼3

bool judge(int *a, int len, int x) 
{ 
    int begin = 0; 
    int end = len - 1; 
    int order = 0;       /*0-升序 1-降序*/
      
    /*判斷升序還是降序*/
    if(a[0] < a[len - 1]) 
        order = 0; 
    else
        order = 1; 
      
    if(order == 0) 
    { 
        while(begin < end) 
        { 
            if(a[begin] + a[end] > x) 
                --end; 
            else if(a[begin] + a[end] == x) 
                return true; 
            else
                ++begin; 
        } 
    } 
    else
    { 
        while(begin < end) 
        { 
            if(a[begin] + a[end] > x) 
                ++begin; 
            else if(a[begin] + a[end] == x) 
                return true; 
            else
                --end; 
        } 
    } 
  
    return false; 
}
View Code

  在這三種寫法中,思路是一致的,且都有一個同樣的毛病,就是啰嗦重復。
  代碼1中:

            switch(Ascending)
            {
                case 0: j++; break;//降序
                case 1: i--; break;//升序
                default:break;
            }

 

            switch(Ascending)
            {
                case 0: i--; break;//降序
                case 1: j++; break;//升序
                default:break;
            }

幾乎雷同的代碼寫了兩次。除此之外,

Ascending = a[1] > a[0] ? 1 : 0;

是錯誤的;

  “構建另一數組”是多余的。

memset(CopyA, 0, sizeof(int) * len);

很傻,不知所雲。

代碼2中

            if(order == 0) 
                --end; 
            else
                ++begin; 

 

            if(order == 0) 
                ++begin; 
            else
                --end; 

也是幾乎同樣的代碼寫了兩次。此外

    if(a[0] < a[len - 1]) 
    { 
        order = 0; 
    } 
    else
    { 
        order = 1; 
    } 

也屬於啰嗦重復代碼。因為前面既然已經

    int order = 0;       /*0-升序 1-降序*/

  這里就完全沒必要再寫if-else語句,只需要寫

    if(a[0] > a[len - 1]) 
    { 
        order = 1; 
    }

就可以了。甚至連這個if語句也不需要寫,只要在聲明order變量時

    int order = a[0] > a[len - 1];       /*0-升序 1-降序*/ 

就可以輕松完成同樣的功能。

代碼3中:

    if(order == 0) 
    { 
        while(begin < end) 
        { 
            if(a[begin] + a[end] > x) 
                --end; 
            else if(a[begin] + a[end] == x) 
                return true; 
            else
                ++begin; 
        } 
    } 
    else
    { 
        while(begin < end) 
        { 
            if(a[begin] + a[end] > x) 
                ++begin; 
            else if(a[begin] + a[end] == x) 
                return true; 
            else
                --end; 
        } 
    } 

啰嗦重復現象更為嚴重。代碼3寫得顯然比代碼2還要差得多。

  造成這種重復現象的原因是,問題給出的條件是“有序”數組,但沒說是升序還是降序。

  前面幾段代碼的算法都是考察數組兩端元素和是否等於指定的X,如和大於X,則拋棄大端元素,形成一個新數組;如和小於X,則拋棄小端元素形成一個新數組。形成新數組后,重復前面步驟繼續考察,直到數組兩端元素和等於X或數組中元素不足2個。

  由於題目沒指定升序還是降序,所以一共有下面4種情況:

 升序(0)   升序(0)  降序(1) 降序(1) 
和>X(1)  和<X(0)  和>X(1)  和<X(0)
拋棄最右元素(1)  拋棄最左元素(0)  拋棄最左元素(0)  拋棄最右元素(1)

  認真觀察一下不難發現,如果升序用0表示,降序用1表示;和>X 用1表示,和<X 用0表示;拋棄最右元素用1表示,拋棄最左元素用0表示的話,很顯然上面表中第3行的值與前兩行的值的異或運算的結果。由此可以簡單地給出如下代碼: 

#include <stdio.h>
#include <stdbool.h>

#define X 31
bool judge(int [], size_t , int ); 

int main( void )
{
  int test[]= { 5,10,21,41,59 } ;//{ 59,41,21,10,5 };
  printf ( "%d%s存在!\n", 
           X ,
           judge( test , sizeof test/sizeof *test, X ) ? "" :"不"
         );
  return 0;
}


bool judge( int a[] , size_t n , int x )
{
   if ( n < 2u )
      return false;

   if ( x == a[0] + a[n-1] )
      return true;

   if ( (x > a[0] + a[n-1]) ^ ( a[0] > a[n-1]) )
       return  judge( a + 1, n - 1 , x )  ;
   
   return  judge( a  , n - 1 , x );
}

 

  前面是用遞歸寫的,不用遞歸也不難實現:

bool judge( int a[] , size_t n , int x ) 
{ 
   while ( n > 1u ) 
   { 
      if ( x == a[0] + a[n-1] ) 
         return true ; 
           
      if ( (x > a[0] + a[n-1]) ^ ( a[0] > a[n-1]) ) 
         a ++ ; 
      n -- ; 
   } 
     
   return false ; 
} 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM