位运算问题

位运算问题

异或 (^ )

只出现一次的数字I 136题:

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

分析:

根据 A ^ A=0 ; 0 ^ A=A 将整个数组俩俩异或 最后剩的就是只出现一次的元素

代码:

public int singleNumber(int[] nums) {
        int a=0;
         for(int i=0;i<nums.length;i++){
             
             a=a^nums[i];
         }
        return a;
}

只出现一次的数字III 260题:

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例:

输入: [1,2,1,3,2,5]
输出: [3,5]

分析:

一次遍历不够,要两次。第一次得到两个单数的异或;第二次利用技巧将数组分成两个集合,两个集合都只有一个单数,其余数都成对。。。

代码:

public int[] singleNumber(int[] nums) {
        int diff=0;
       
       //第一趟遍历  和I相同 目的是求出所求的两个单身数的XOR
        for (int num:nums) {    
            diff^=num;
        }
        diff&=-diff;      //elegent!因为上一步后diff中肯定至少有一位为1  
        //这一步的目的是找到最靠右的那个1(当然不找最右的也一样)作为二趟遍历中的判断标志位   例如diff为:000100
       
        int[] res={0,0};          //保存要找的那两个单身数的数组
       
       //第二趟遍历
        for (int num:nums){    
            if ((num&diff)==0){      /*判断是为了分成两个集合XOR 这两个集合中各自包含一个单身数 
                                       并且每个集合中除了那个单身数都是成对的(两个相同的数对于diff中的那一个标志位                                       一定也一样啊,所以一定会判断到一个集合中)
                                        每个集合内全部XOR就能得到那个单身数
                                     */
                res[0]^=num;
            }else {
                res[1]^=num;
            }
        }
        return res;
    }

只出现一次的数字II 137题:

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

分析:

这题参考博客上的做法 好理解一些: 考虑每个元素的为一个32位的二进制数,这样每一位上出现要么为1 ,要么为0。对数组,统计每一位上1 出现的次数count,必定是3N或者3N+1 次。让count对3取模,能够获得到那个只出现1次的元素该位是0还是1

还有一种简单的异或方法 discuss

代码:

 public int singleNumber(int[] nums) {
        int res=0;
        for (int i=0;i<32;i++){
            int mask=1<<i;
            int count=0;
            for (int j=0;j<nums.length;j++){
                if ((nums[j]&mask)!=0){
                    count++;
                }
            }
            if (count%3!=0){
                res=res|mask;
            }
        }
        return res;
    }

求众数 169题:

给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。

分析:

这题方法很多 排序,哈希表,摩尔投票,count bits,位运算。用count bits和137题差不多,也是32位,按位统计(比较慢)。

代码:

位运算:

public int majorityElement(int[] num) {

    int ret = 0;

    for (int i = 0; i < 32; i++) {

        int ones = 0, zeros = 0;

        for (int j = 0; j < num.length; j++) {
            if ((num[j] & (1 << i)) != 0) {
                ++ones;
            }
            else
                ++zeros;
        }

        if (ones > zeros)
            ret |= (1 << i);
    }
    
    return ret;
}

摩尔投票:

 public int majorityElement(int[] nums) {
        int major=nums[0],count=1;
        for (int i = 1; i <nums.length ; i++) {
            if (count==0){
                major=nums[i];
                count++;
            }else if (major==nums[i]){
                count++;
            }else count--;
            
        }
        return major;
    }

缺失数字 268题:

给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。

示例:

输入: [9,6,4,2,3,5,7,0,1]
输出: 8

分析:

这题主要的方法有两种:哈希表 或者 位元算 。相比之下位运算的空间复杂度只有O(1),所以更好。
利用异或性质 A ^ B ^ B=A;

代码:

public int missingNumber(int[] nums) {
        int res=nums.length;
        for (int i = 0; i <nums.length ; i++) {
            res=res^i^nums[i];
        }
        return res;
    }

移位 (<< ; >> ; >>>)

颠倒二进制位 190题:

颠倒给定的 32 位无符号整数的二进制位。

示例:

输入: 43261596
输出: 964176192
解释: 43261596 的二进制表示形式为 00000010100101000001111010011100 ,
     返回 964176192,其二进制表示形式为 00111001011110000010100101000000 。

分析:

右移位,每次取最低位;加到结果,结果左移位。(32bit)

代码:

public int reverseBits(int n) {
        int res=0;
        for (int i = 0; i <32 ; i++) {
            res=res<<i+(n&1);
            n=n<<1;
        }
        return res;
    }

位1的个数 191题:

编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

分析:

注意条件中的无符号整数条件。思路就是移位,&1,逐位统计。

还要注意这里的移位:右移有两种,一种无符号右移位>>>;一种有符号右移位>> 。区别在于移位后拿什么补空位,>>是哪符号位补(0或1),>>>拿0补

<<      :     左移运算符,num << 1,相当于num乘以2

>>      :     右移运算符,num >> 1,相当于num除以2

>>>    :     无符号右移,忽略符号位,空位都以0补齐

代码:

public int hammingWeight(int n) {
        int count=0;
        while (n!=0) {
            if ((n&1)!=0){
                count++;
            }   
            n=n>>>1;
        }
        return count;
    }

数字范围按位与 201题:

给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。

分析:

当m!=n时,从m到n中肯定存在一个奇数一个偶数,而 奇数&偶数 的最后一位为0,所以最后结果的最后一位一定为零。我们将m和n的结尾cut掉,再赋值给m和n,直到m==n ,最后再在m后边补上等于被cut掉的位数的0。

代码:

public int rangeBitwiseAnd(int m, int n) {
      int i = 0; // i means we have how many bits are 0 on the right
      while(m != n){
        m >>= 1;
        n >>= 1;
        i++;  
      }  
      return m << i;  
    }

与 (&)

2的幂 231题:

给定一个整数,编写一个函数来判断它是否是 2 的幂次方。

示例:

输入: 1
输出: true
解释: 2^0 = 1

分析:

如果一个数(正数)是2的幂,那么它的二进制表示中一定只有一个1。所以可以统计二进制中1的个数来判断,但更简单的是 n&(n-1)看结果是否为0。

代码:

public boolean isPowerOfTwo(int n) {
         if(n<=0) return false;
        if((n&(n-1))==0){
            return true;
        }
        else return false;
    }

原文地址:https://www.cnblogs.com/10zhang/p/9598223.html