zoukankan      html  css  js  c++  java
  • C#位运算及3道要求高效的算法题

    在C#中可以对整型运算对象按位进行逻辑运算。按位进行逻辑运算的意义是:依次取被运算对象的每个位,进行逻辑运算,每个位的逻辑运算结果是结果值的每个位。


    C#支持的位逻辑运算符如下表所示。

    运算符号 意义 运算对象类型 运算结果类型 对象数 实例


    ~ 位逻辑非运算 整型,字符型 整型 1 ~a


    & 位逻辑与运算 2 a & b


    | 位逻辑或运算 2 a | b


    ^ 位逻辑异或运算 2 a ^ b


    << 位左移运算 2 a<<4


    >> 位右移运算 2 a>>2

    1、位逻辑非运算


    位逻辑非运算是单目的,只有一个运算对象。位逻辑非运算按位对运算对象的值进行非运算,即:如果某一位等于0,就将其转变为1;如果某一位等于1,就将其转变为0。
    比如,对二进制的10010001进行位逻辑非运算,结果等于01101110,用十进制表示就是:
    ~145等于110;对二进制的01010101进行位逻辑非运算,结果等于10101010。用十进制表示就是~85等于176。


    2、位逻辑与运算


    位逻辑与运算将两个运算对象按位进行与运算。与运算的规则:1与1等于1,1与0等于0。
    比如:10010001(二进制)&11110000等于10010000(二进制)。


    3、位逻辑或运算


    位逻辑或运算将两个运算对象按位进行或运算。或运算的规则是:1或1等1,1或0等于1,
    0或0等于0。比如10010001(二进制)| 11110000(二进制)等于11110001(二进制)。


    4、位逻辑异或运算


    位逻辑异或运算将两个运算对象按位进行异或运算。异或运算的规则是:1异或1等于0,
    1异或0等于1,0异或0等于0。即:相同得0,相异得1。
    比如:10010001(二进制)^11110000(二进制)等于01100001(二进制)。


    5、位左移运算


    位左移运算将整个数按位左移若干位,左移后空出的部分0。比如:8位的byte型变量
    byte a=0x65(即二进制的01100101),将其左移3位:a<<3的结果是0x27(即二进制的00101000)。


    6、位右移运算


    位右移运算将整个数按位右移若干位,右移后空出的部分填0。比如:8位的byte型变量
    Byte a=0x65(既(二进制的01100101))将其右移3位:a>>3的结果是0x0c(二进制00001100)。

    在进行位与、或、异或运算时,如果两个运算对象的类型一致,则运算结果的类型就是运算对象的类型。比如对两个int变量a和b做与运算,运算结果的类型还是int型。如果两个运算
    对象的类型不一致,则C#要对不一致的类型进行类型转换,变成一致的类型,然后进行运算。
    类型转换的规则同算术运算中整型量的转换则一致。
    由位运算符连接整型量而成的表达式就是位运算表达式。  

    以上部分转自:http://blog.csdn.net/phoebeofhust/archive/2008/04/06/2255358.aspx

    高级的编程语言往往能让我们直接访问变量的位,而计算机本身内部就是二进制的表示,所以对位操作,对计算机而言当然是极有效率。和2有关的题目很可能需要使用位运算来计算, 效率更高。 下边是从园友:高歌的博客文章摘录整理出的2道和bitewise logical operation有关系的算法题及解题思路。

    问题1:请写一个方法判断一个整数是奇数还是偶数。

    分析:

    如果答案就是下面这个方法


    public static bool IsEven(int number)
    {
        
    return ((number % 2== 0);
    }

    貌似我也就不用多费口舌了,这样的问题,地球人都能回答出来。

    既然我们已经提到位运算,当然要关注一下奇数和偶数究竟有何不同,奇数的最后一位总是1,比如奇数3,二进制形式为00000011(省略前面24个0),而偶数的最后一位总是0,比如偶数6,二进制形式为00000110。所以我们现在只要检测数字的最低位是否为0,就可以知道它是不是偶数了。

    那么,怎么判断最低位是0呢?通常的方案是将最低位和1相与,如果结果为0,则为偶数,否则为奇数

    比如奇数3和1位与,实际上是

      00000000 00000000 00000000 00000011

    &00000000 00000000 00000000 00000001

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

      00000000 00000000 00000000 00000001

    再比如偶数6和1位与,实际上是

      00000000 00000000 00000000 00000110

    &00000000 00000000 00000000 00000001

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

      00000000 00000000 00000000 00000000

    所以我们的最终答案可能如下


    /// <summary>
    /// 判断一个整数是否是偶数
    /// </summary>
    /// <param name="number">传入的整数</param>
    /// <returns>如果是偶数,返回true,否则为false</returns>

    public static bool IsEven(int number)
    {
        
    return ((number & 1== 0);
    }


    /// <summary>
    /// 判断一个整数是否是奇数
    /// </summary>
    /// <param name="number">传入的整数</param>
    /// <returns>如果是奇数,返回true,否则为false</returns>

    public static bool IsOdd(int number)
    {
        
    return !IsEven(number);
    }

    问题2:请写一个方法判断一个整数是否是2的n次方。 

    相信你能马上写出如下的算法


    public static bool IsPower(int number)
    {
        
    if (number <= 0)
        
    {
            
    return false;
        }


        
    while (true)
        
    {
            
    if (number == 1)
            
    {
                
    return true;
            }

            
    //如果是奇数
            if ((number & 1== 1)
            
    {
                
    return false;
            }

     
    //右移一位
            number >>= 1;
        }

    }


    public static void Main()
    {
        Console.WriteLine(IsPower(
    -8));
        Console.WriteLine(IsPower(
    0));
        Console.WriteLine(IsPower(
    1));
        Console.WriteLine(IsPower(
    2));
        Console.WriteLine(IsPower(
    5));
        Console.WriteLine(IsPower(
    9));
        Console.WriteLine(IsPower(
    12));
        Console.WriteLine(IsPower(
    16));
    }

    初学写成这样确实不错,我们用到了位运算,但事实上,貌似和没有用的时候算法效率差不多。(提一个额外的话题,在写程序时尽量使用复合赋值运算符号,比如 i++的效率比 i += 1要高, i += 1 又比 i = i + 1要高)

    但再仔细考虑一下,2的n次方会有什么特点?那就是其二进制表示只有1个1,那我们又怎么能知道其二进制表示只有1个1呢?

    想了两天,突然想到,如果是整数8,其二进制表示形式是00001000,减去1后为00000111,而这两者位与的结果刚好是0

    所以就有了下面的算法


    public static bool IsPower(int number)
    {
        
    if (number <= 0)
        
    {
            
    return false;
        }


        
    if ((number & (number - 1)) == 0)
        
    {
            
    return true;
        }


        
    return false;
    }

    这个算法的效率一次循环都没做当然要比上面的高(事实上下面的算法时间复杂度是O(1),而上面的算法时间复杂度是O(n))。

    问题3: 对字节变量,其二进制表示法中求有多少个1,如 00101010则返回值为 3,也是要求效率最高。

    代码
            private static int GetCountGroupByOne(int data)
            {
                
    int count = 0;

                
    if (data == 0)
                {                
                }
                
    else if (data > 0)
                {
                    
    while (data > 0)
                    {
                        data 
    &= (data - 1);
                        count
    ++;
                    }
                }
                
    else
                {
                    
    int minValue = -0x40000001;
                    
    while (data > minValue)
                    {
                        data 
    &= (data - 1);
                        count
    ++;
                    }

                    count
    ++;
                }

                
    return count;
            }

    注: 以上3个问题均摘自: http://www.cnblogs.com/amandag/, 其中第3个问题的解法由浪子无晴我本人提供.

  • 相关阅读:
    231. Power of Two
    204. Count Primes
    205. Isomorphic Strings
    203. Remove Linked List Elements
    179. Largest Number
    922. Sort Array By Parity II
    350. Intersection of Two Arrays II
    242. Valid Anagram
    164. Maximum Gap
    147. Insertion Sort List
  • 原文地址:https://www.cnblogs.com/Langzi127/p/1773895.html
Copyright © 2011-2022 走看看