巧用“异或”

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

 

posted @ 2013-10-02 00:11  garbageMan  阅读(2750)  评论(6编辑  收藏  举报