巧用“异或”

  “异或”运算是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 ; 

} 

 

你可能感兴趣的:(巧用“异或”)