• 巧用“异或”


      “异或”运算是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存在!
    ", 
               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 ; 
    } 
    
  • 相关阅读:
    常用算法编程题目学习与训练的网站
    ES6的JavaScript算法思想实现之分而治之,动态规划,贪心算法和回溯算法
    ES6的JavaScript算法实现之排序、搜索和随机算法
    ES6的JavaScript数据结构实现之图
    ES6的JavaScript数据结构实现之二叉堆和堆排序
    ES6的JavaScript数据结构实现之树(二叉搜索树、AVL树、红黑树)
    ES6的JavaScript数据结构实现之递归
    ES6的JavaScript数据结构实现之字典与散列表
    ES6的JavaScript数据结构实现之集合
    ES6的JavaScript数据结构实现之链表
  • 原文地址:https://www.cnblogs.com/pmer/p/3348926.html
Copyright © 2020-2023  润新知