java中的位运算

  • 前言
  • 一、基础知识1
  • 二、练习1
  • 三、基础知识2
  • 四、练习2
  • 总结


前言

1)异或
2)原码、反码、补码

一、基础知识1

  1. 异或运算
    按位相同为0,相反为1.
    4 ^ 5 = 100 ^ 101 = 001
  2. 运算特点
    a)交换律,a ^ b ^ c = a ^ c ^ b
    b)0 ^ n = n
    c)n ^ n = 0
    a)+b)+c):a ^ b ^ c ^ b = a ^ (b ^ b) ^ c = a ^ 0 ^ c = a ^ c

二、练习1

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
通过异或来完成相同元素的抵消。以Space – O(1)来完成。
a ^ a ^ b ^ b…y ^ y ^ z = 0 ^ 0 ^ … ^ z = z

public int singleNumber2(int[] nums) {
        //res=0,0^n=n
        int res = 0;
        for(int num:nums){
            res ^= num;
        }
        return res;
    }

注:其它解法
1)HashSet添加set中未有元素、删除set中已在元素,最终得到只出现了单次的元素。(Time:O(n),Space:O(n))
2)将数组排序,判断相邻元素是否相等,来得到单一元素。(Time:O(nlog2n),Space:O(1))
3种解法都是在利用所给条件的特性+所学的基础知识下产生,所以我们不仅需要沉淀基础知识,还需锻炼分析转化问题的能力。(学习的两大核心)

三、基础知识2

  1. 原码、反码、补码
    原码,即二进制定义所表示一个数。1->0001;2->0010;3->0011;…
    反码,按位取反(符号位不算)。~1->1110;~2->1101;~3->1100
    补码,反码+1。1111;1110;1101;注:Java中数据用补码存储。
    正数,最高位用0表示,源码,反码、补码相等。
    负数,最高位用1表示,源码、反码、补码不相等。
    注:对于+0、-0,只用+0,-0用来表示另一个负数,所以同等bit表示,负数比正数多1,所以将负数转正数时需防止溢出。(以8bit为例)-128 到 127;即1 0000000 到 0 0000000。
    想将Integer.Min_Value 转为正数时,需先将Integer.Min_Value用long来装入。

四、练习2

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
同样可以用HashSet、排序来解决,但是Time或Space复杂度较高。
采用异或。
1)同样将全部值异或在一起,有两个的就抵消,所以得到两个单值的异或值。
2)如何从一个异或结果值拆成两个值?通过取最低有效位(least significant bit)。取到最低位有1的地方(当然取任何一个1都可以,毕竟异或时,该位能为1,说明这两个值的该位一个为1一个为0).
3)将所有数分组,分为两组,固定位为1、固定位为0。(a b b c c d d e:{a b b c c }+{e d d },相同的会抵消,可简化为{ a }+{ e })

public int[] singleNumber(int[] nums) {
        int xorsum = 0;
        for (int num : nums) {
            xorsum ^= num;
        }
        // 防止溢出(同等位数,负数比正数多1.如integer:2^31^ - 2^31 -1(因为+0占了一位正数))
        //Integer.Min_Value最低有效位就是最高有效位。(只有符号位为1:-2^31^:0x8(1000)0000000)
        //为什么这里(xorsum)&(-xorsum)能取到最低有效位(least significant bit,lsb)?参考上面的补码.
        //4:0 000 0100(原码);0 000 0100(补码);-4:1 000 0100(原码);1 111 1011 + 1 = 1 111 1100(补码)
        //0 000 0100 & 1 111 1100 = 0 000 1000(高位全为0,通过补码特性留住了最低有效位)
        int lsb = xorsum == Integer.MIN_VALUE ? Integer.MIN_VALUE: xorsum & (-xorsum);
        //通过上面找到的固定位数是否为1来分类。
        int type1 = 0, type2 = 0;
        for (int num : nums) {
            if ((num & lsb) != 0) {
                type1 ^= num;
            } else {
                type2 ^= num;
            }
        }
        return new int[]{type1, type2};
    }

总结

1)异或
2)原码、反码、补码
3)沉淀基础知识 + 锻炼分析转化问题