java之位运算符在算法中的简单应用(附算法题:只出现一次的数字)

位运算符有

  • &
    按位与
    如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
  • |
    按位或
    两个相应的二进制位中只要有一个为1,该位的结果值为1
  • ^
    按位异或
    若参加运算的两个二进制位值相同则为0,否则为1
  • ~
    取反
    ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1
  • <<
    左移
    用来将一个数的各二进制位全部左移N位,右补0
  • >>
    右移
    将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数, 高位补0(正数)或1(负数)
  • >>>
    右移
    将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数, 高位补0

先引入一些别人对位运算的总结:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.m的n次方

//时间复杂度近为 O(logn),而且看起来很牛逼。
int pow(int n){
    int sum = 1;
    int tmp = m;
    while(n != 0){
        if(n & 1 == 1){
            sum *= tmp;
        }
        tmp *= tmp;
        n = n >> 1;
    }
    
    return sum;
}

————————————————
版权声明:本文为CSDN博主「帅地」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_37907797/article/details/103120886

今天遇到的题目是这样的(就是上面大佬总结的第3个方法的升级版):在这里插入图片描述

在这里插入图片描述


class Solution {
    public int[] singleNumber(int[] nums) {
        int xorsum = 0,a=0,b=0;
        for (int num : nums) {
            xorsum ^= num;
        }
      int div=1;
      while((xorsum & div)==0){
          div=div<<1;
      }
      for(int i=0;i<nums.length;i++){
          if((nums[i] & div)==0){
              a=a ^nums[i];
          }else{
            b=b^nums[i];
          }
      }
        return new int[]{a, b};
    }
}


知识基础:a ^ b ^ b=a,且满足结合律和分配律(b ^ a ^ b=a),故相同的数是可以直接消除的。很容易得到最后两个不同数字的^(异或)结果。所以关键是怎么区分他们呢?
先说实例:3(0011),5(0101)
3^5=0110(6),我就可以用倒数第二位的那个1作为3跟5的区别对他们进行不同的分组,用0010和3(0011)做&与运算,得到的是0010(非0)。用0010和5(0101)做&与运算,得到的是0000(为0)。

再说道理:两个数的异或值中,为1的那几位数就是这两个数的不同点,只要找到是第几位为1,然后用这个位数跟这两个数进行与运算,得到的值一定不同。

  1. 将数组每个值都进行异或运算,相同的为0,最后得到的就是那两个独立的值的异或结果记为xorsum。
  2. 数字1(div)的二进制为0001,用它末尾的那个1与xorsum进行&与运算,得到为0,说明xorsum的最后一位为0,我们要找到xorsum中为1的那一位,所以让数字1进行左移<<,重复判断得到div(例如可能是0010),用div这个数与数组元素进行与运算,就可以把需要找的那两个数分开为两组了。

可能说的不是太清楚,但我真的尽力了,只能说我自己心里清楚了,位运算确实有点东西。我第一想法其实是用HushMap做的,但空间复杂度有点高。

最后有个更难理解的题,:
在这里插入图片描述

class Solution {
    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;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/WGki4K/solution/zhi-chu-xian-yi-ci-de-shu-zi-by-leetcode-0vrt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

答案是这样的,
在这里插入图片描述
我感觉还是HushMap更好理解,哭唧唧

class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer, Integer> freq = new HashMap<Integer, Integer>();
        for (int num : nums) {
            freq.put(num, freq.getOrDefault(num, 0) + 1);
        }
        int ans = 0;
        for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
            int num = entry.getKey(), occ = entry.getValue();
            if (occ == 1) {
                ans = num;
                break;
            }
        }
        return ans;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/WGki4K/solution/zhi-chu-xian-yi-ci-de-shu-zi-by-leetcode-0vrt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JJpZh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值