LeetCode位运算(找出落单的数,二进制中1的个数,2的幂等)

位运算理论+技巧介绍

对于位运算的基础还有不熟悉的可以参考这篇:位运算操作符
这里直接给力扣中的常用技巧!

1.与 &

表达式含义
(x & 1) == 1奇数,等价于 x % 2 == 1
(x & 1) == 0偶数,等价于 x % 2 == 0
x & (x - 1)把x最低位的二进制1给去掉
x & -x获取x最低位的1

解释:
设x=10=00001010,则x-1=9=00001001,-x= -10 = 11110110

把最低位二进制1去掉
00001010(x)
&
00001001(x-1)
结果是:00001000,即把原来的x=10的最低位的二进制1去掉了
应用:lc191 位1的个数,还可以统计二进制中1的个数

获取x最低位的1
00001010(x)
&
11110110(-x)
结果是:00000010,即获取了原来x=10的最低位的1

2.异或 ^

性质表达式
交换律a ^ b ^ c <=> a ^ c ^ b
任何数与 0 异或 为任何数0 ^ n => n
相同的数 异或 为0n ^ n => 0

解释:
相同的数 异或 为0
首先明确异或的概念,相同则为0,不同则为1。所以相同的数异或为0
应用:lc136 只出现一次的数字,lc268 丢失的数字

任何数与 0 异或 为任何数
如果这个数的对应位是0,则结果为0,如果对应位是1,则结果是1,所以还是任何数

3.移位及综合运用(指定位置)

功能表达式
x ∗ 2 n x * 2^n x2nx << n
x / 2 n x / 2^n x/2nx >> n
将 x 最右边的n位清零x & (~0 << n)
获取 x 的第n位二进制值(x >> n) & 1(从低到高位算,从第0位开始)
获取 x 的第n位的幂值x & (1 << n)
仅将 x 的第n位 置为1x | (1 << n)
仅将 x 第n位置为0x & (~(1 << n))
将 x 最高位至第n位(含)清零x & ((1 << n) - 1)
将 x 的第n位至第0位(含)清零x & (~((1 << (n + 1)) - 1))

4.同或

注意Java中没有同或,可以用异或取反

开撸

1. lc136 只出现一次的数字

力扣136链接

描述:

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素

示例:

输入: [4,1,2,1,2]   输出: 4

Solution
此题在哈希表中也出现过,leetcode哈希表(哈希集合,哈希映射)
当时第一反应肯定是用HashSet的去重性

但是如果想要线性复杂度,就要想到用位运算。利用的就是异或的性质

  public int singleNumber(int[] nums) {
        int res=0;   // 0与任意数异或为任意数
        
        // 对每个数进行相互异或,相同的全变成了0
        for(int n:nums)
        res^=n;
        // 最后只剩下那个只出现一次的数字
        return res;
    }

2. lc137 只出现一次的数字II

力扣137链接

描述:

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

示例:

输入:nums = [0,1,0,1,0,1,99]   输出:99

Solution:

与上题很相似,但又觉得有些困难。当然,可以用哈希的方法(关于次数的题目大多和哈希有关)

还是从原始出发,依次计算最终答案的各个二进制位,一张图表明:

在这里插入图片描述

1.对于出现三次的数字,各位出现的次数都是3的倍数

2.统计所有数字的各二进制位中1的个数,并对3取余,结果位只出现一次的数字

3.对于数组中的每一个元素 x,我们使用位运算(x >> i) & 1 得到 x 的第 i 个二进制位(将第 i 位移到最低位与1相与,得到结果,因为1和0 和1 相与 为其自身)

4.将它们相加再对 3取余,得到的结果一定为 0 或 1,即为答案的第 i 个二进制位

代码

 public int singleNumber(int[] nums) {
    // 初始化一个数组,用来记录结果的各个二进制位
     int[] tmp = new int[32];
     // 统计所有数字的各个二进制位的个数
        for(int num:nums)
        {
            for(int i=0;i<32;i++)
            {
            // 将第 i 位移到最低位与1相与,得到结果
                if(((num>>i)&1)==1)
                    tmp[i]++;
            }
        }
        // ans 位最终结果
        int ans=0;
        for(int i=0;i<32;i++)
        {
        // 对于各个位不为0的
        if ((tmp[i] % 3 & 1) == 1) {
              // 将二进制转化位十进制,1<<i=2^i表示权重
                ans+= (1 << i);
            }
        }
   return ans;
}

稍微改进一下,不用辅助空间,直接处理:

 public int singleNumber(int[] nums) {
        int ans = 0;
        for (int i = 0; i < 32; ++i) {
            int total = 0;
            for (int num: nums) {
                total += ((num >> i) & 1);
            }
            if (total % 3 != 0) {
                ans |= (1 << i);
            }
        }
        return ans;
    }

3. lc260 只出现一次的数字III

力扣260链接

描述:

给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

示例:

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

Solution:
要想用异或,就得分组,首先明确剩下的两个数必定有某一位不相同,所以可以把这些数全部异或,得到的数的二进制如果某位是1则代表这两个数的这一位不同,所以可以通过这一位来分组。又因为1可能不只一位,所以可以直接取最低位(x&-x)

public int[] singleNumber(int[] nums) {
        int xorsum = 0;

        for(int num:nums)
          xorsum ^= num;

        int tmp = xorsum & (-xorsum);
        int num1=0,num2=0;
        for(int num:nums)
        {
            if((tmp & num) ==0)
                num1 ^= num;
            else
                num2 ^= num;
        }
        return new int[]{num1,num2};
    }

4. lc268 丢失的数字

力扣268链接

描述

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

示例

输入:nums = [3,0,1]   输出:2

Solution:
此题与lc136 有异曲同工之妙,只需要多想一步
将这n个数与1~n 这n+1个数放在一起,共2n+1个数一起异或,最后的结果就是缺失的数

public int missingNumber(int[] nums) {
     int res = 0;
     for(int i=0;i<nums.length;i++)
     {
         res ^= i;
         res ^= nums[i];
     }
     // 这里如果一开始的res初始化为数组长度,那最后输出就直接res
     return res^nums.length;
}

当然,本题还有其他方法,例如数学方法,求1-n的所有和,减去目前数组的和
还可以先排序再用对应下标的方法(如果没有缺失,那相应下标就是数组的中值)

5. lc191 位1的个数

描述

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

示例

输入:00000000000000000000000000001011
输出:3

Solution:
利用最开始总结的技巧:n&(n-1) 去除二进制的低位1来计算二进制的1

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

6. lc 231 2的幂

力扣231链接

描述

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。

示例

输入: n = 16   输出: true

Solution1:
和上一题有异曲同工之妙

因为2的幂次方的二进制只有1位是1,所以把最低位去掉之后就成了0。利用上面提到的 n & (n-1)

 public boolean isPowerOfTwo(int n) {
        return n>0 && ((n & (n-1))==0);
    }

Solution2:
利用n & (-n) 可以获取最低位的1

public boolean isPowerOfTwo(int n) {
        return n > 0 && (n & -n) == n;
    }

7. lc1356 根据数字二进制下 1 的数目排序

力扣1356链接

描述

给你一个整数数组 arr 。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。
请你返回排序后的数组。

示例

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

Solution:

链接:https://leetcode.cn/problems/power-of-two/solution/5chong-jie-fa-ni-ying-gai-bei-xia-de-wei-6x9m/

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值