力扣刷题记录_位运算(自学)

位运算

    • 1.总结
      • 1.1.异或
      • 1.2.不用额外变量交换两个整数
      • 1.3.所有偶数位为和所有奇数位为1
      • 1.4.针对数组中元素两两组合的写法
      • 1.4.Brian Kernighan 算法
    • 2.异或(力扣136)
    • 3.汉明距离(力扣461)
    • 4.只出现一次的数字(力扣268)
    • 5.只出现一次的数字 III(力扣260)
    • 6.颠倒二进制位(力扣190)
    • 7.2的幂(力扣231)
    • 8.4的幂(力扣342)
    • 9.交替位二进制数(力扣693 )
    • 10.数字的补数(力扣476)
    • 11.两整数之和(力扣371)
    • 12.最大单词长度乘积(力扣318)
    • 13.比特位计数(力扣338)

1.总结

1.1.异或

异或运算有以下三个性质。
1.任何数和 0做异或运算,结果仍然是原来的数,即a^0=a
2.任何数和其自身做异或运算,结果是 0,即a^a=0
3.异或运算满足交换律和结合律,即a^b^a=b^a^a=b^(a^a)=b^0=b

1.2.不用额外变量交换两个整数

给定两个整数a和b
a = a ^ a ^ b = b;
b = b ^ b ^ a = a;

a = a ^ b;
b = a ^ b;
a = a ^ b;

1.3.所有偶数位为和所有奇数位为1

0xaaaaaaaa 和 0xAAAAAAAA 一样,表示所有偶数位为1
0x55555555表示所有奇数位为1

1.4.针对数组中元素两两组合的写法

		for (int i = 0; i < len - 1; i++) {
            for (int j = i + 1; j < len; j++) {
                
            }
        }
		for(int i = 0;i < len;i ++){
            for(int j = 0;j < i;j ++){
                
            }
        }

1.4.Brian Kernighan 算法

Brian Kernighan算法可以用于清除二进制数中最右侧的1。x&(x-1)
1.x=111,x-1 = 110,x&(x-1)=110,清除了最右侧的1
2.x=110,x-1 = 101,x&(x-1)=100,清除了最右侧的1

2.异或(力扣136)

//1.异或运算
    public int singleNumber(int[] nums) {
        int len = nums.length;
        int res = nums[0];
        for(int i = 1;i < len;i ++){
            res ^= nums[i];
        }
        return res;
    }

3.汉明距离(力扣461)

//1.利用javaAPI
    public static int hammingDistance(int x, int y) {
        return Integer.bitCount(x ^ y);
    }
//2.移位计算
    public static int hammingDistance2(int x, int y) {

        int z = x ^ y;
        int count = 0;
        while (z > 0){
            //看z的最后一位是否为1
            count += z & 1;
            //z右移
            z >>= 1;
        }
        return count;
    }
//3.Brian Kernighan 算法(只统计1的个数,略过0)
    public static int hammingDistance3(int x, int y) {

        int z = x ^ y;
        int count = 0;
        while (z != 0){
            z &= z - 1;
            count ++;
        }
        return count;
    }

4.只出现一次的数字(力扣268)

//1.数学方法
    public static int missingNumber(int[] nums) {

        int sum = 0;
        for (int i : nums){
            sum += i;
        }
        int len = nums.length;
        return len * (1 + len) / 2 - sum;
    }

//2.位运算
    public static int missingNumber(int[] nums) {

        int len = nums.length;
        int sum = 0;
        for (int i = 0; i < len; i++) {
            sum ^= (i ^ nums[i]);
        }
        return sum ^ len;
    }

5.只出现一次的数字 III(力扣260)

//1.哈希集合(最先想到的方法)
    public static int[] singleNumber(int[] nums) {

        //或者是用map也可以
        Set<Integer> set = new HashSet<>();
        for (int i : nums){
            if (set.contains(i)){
                set.remove(i);
            }else{
                set.add(i);
            }
        }

        int[] res = new int[2];
        int j = 0;
        for (int i : set){
            res[j ++] = i;
        }
        return res;
    }
//2.位运算
    /*
    * 就是通过先求二进制异或和,选择一个为1的位,那么两个数在这个位上一
    * 定是一个为1一个为0,通过与运算就可以把它们分组,就变成了只出现一次
    * 的数字Ⅰ问题,再异或一次就好
    * */
    public static int[] singleNumber2(int[] nums) {

        int nAdd = 0;
        for (int num : nums){
            nAdd ^= num;
        }

        int i = 1;
        while ((i & nAdd) == 0){
            i <<= 1;
        }

        int nAdd1 = 0,nAdd2 = 0;
        for (int num : nums){
            if ((i & num) != 0){
                nAdd1 ^= num;
            }else{
                nAdd2 ^= num;
            }
        }
        return new int[]{nAdd1,nAdd2};
    }

6.颠倒二进制位(力扣190)

//1.位运算
    public int reverseBits(int n) {
        int res = 0;
        for(int i = 0;i < 32;i ++){
            if(n == 0) break;
            //找到为1的位并反转
            int temp = ((n & 1) << (31 - i));
            //将所有反转之后的1组合
            res |= temp;
            //n无符号右移
            n >>>= 1;
        }
        return res;
    }
//2.调用javaAPI
    public int reverseBits2(int n) {
        return Integer.reverse(n);
    }

7.2的幂(力扣231)

//1.自己想的,统计1出现的次数
    public boolean isPowerOfTwo(int n) {
        int countOne = 0;
        while(n > 0){
            countOne += (n & 1);
            n >>= 1;
        }
        return countOne == 1 ? true : false;
    }
//2.技巧
    public static boolean isPowerOfTwo2(int n) {
        //如果n是2的幂次方,则n和n-1的每一个对应位必定是一个为0一个为1
        return n > 0 && (n & (n - 1)) == 0;
    }
//3.技巧
    public static boolean isPowerOfTwo3(int n) {
        //如果n是2的幂次方,则n和-n按位做与,最终结果定是n
        return n > 0 && (n & -n) == n;
    }
//4.技巧
    public static boolean isPowerOfTwo4(int n) {
        return n > 0 && Integer.bitCount(n) == 1;
    }

8.4的幂(力扣342)

//1.使用循环
    public static boolean isPowerOfFour(int n) {

        while ( n > 0 && Integer.bitCount(n) == 1){
            if ((n & 1) == 1){
                return true;
            }
            n >>= 2;
        }

        return false;
    }
//2.技巧
    public static boolean isPowerOfFour2(int n) {
        //0xaaaaaaaa  和   0xAAAAAAAA 一样,表示所有偶数位为1
        return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;
//        0x55555555表示所有奇数位为1
//        return n > 0 && (n & (n - 1)) == 0 && (n & 0x55555555) != 0;
    }
//3.技巧
    public static boolean isPowerOfFour3(int n) {
        //是2的幂次方并且模上3为1,则为4的幂次方
        //假设4的幂次方为4^x,则4^x % 3 = 4^(x-1) * (4%3) = 4^(x-1),所以最终为1
        return n > 0 && (n & (n - 1)) == 0 && n % 3 == 1;
    }

9.交替位二进制数(力扣693 )

//1.自己想的
    public static boolean hasAlternatingBits(int n) {
        //看相邻位是否相同
        while (n > 0){
            if ((n & 1) == (n >> 1 & 1)){
                return false;
            }
            n >>= 1;
        }
        return true;
    }
/*2.
     * 分析:
     * 如果n是交替的01,对于它右移一位后得到的m,
     * 存在n跟m在二进制下必然是0和1对应的(对位)。异或运算必定都是1;
     *
     * 举个栗子:5=101 5>>1=10,5^(5>>1)=111
     *  101
     *   10  =111
     *假设异或结果共有N位为1,则将此结果加1之后,第N+1位为1,从1到N位都为0,
     *此时再将两者做与运算,则最终结果必为0
     */
    public static boolean hasAlternatingBits2(int n) {
        int temp=n^(n>>1);
        return (temp&(temp+1))==0;
    }
//3.和1的思路是一样的
    public static boolean hasAlternatingBits3(int n) {

        while (n > 0){
            if ((n % 2 == n / 2 % 2)){
                return false;
            }
            n /= 2;
        }
        return true;
    }

10.数字的补数(力扣476)

//1.自己想的
    public int findComplement(int num) {
        int res = 0;
        //用来记录当前到了第几位
        int count = 0;
        while(num > 0){
            //如果当前位为1,则取反为0,不计数
            if((num & 1) == 1){
                count ++;
            }else{
                //如果当前位为0,则取反为1,计数
                res += (1 << count);
                count ++;
            }
            num >>= 1;
        }
        return res;
    }
//2.求掩码
    /*
    思路:
    5 -> 101 , 其补码为 010 ,将 101 与 111 做 异或即可达到他对应的补码
    问题的关键为:求掩码
     */

    /*
    1     ->     1
    11    ->     3
    111   ->     7
    1111  ->     15
    ...
    * */
    public static int findComplement2(int num) {

        int pre = 1,i = 1;
        while (pre < num){
            pre += Math.pow(2,i);
            i ++;
        }
        return pre ^ num;
    }
//3.思路和1一样
    public static int findComplement3(int num) {

        int temp = num;
        int pre = 0;
        while(temp > 0){
            temp >>= 1;
            pre = (pre << 1) + 1;
        }
        return num ^ pre;
    }

11.两整数之和(力扣371)

//1.位运算
    //可以将b作为进位来看待
    public int getSum(int a, int b) {
        //如果存在进位,就要加入到总和中去
        while(b != 0){
            //计算对应位能形成进位的位置
            int temp = (a & b) << 1;
            //计算对应位不能形成进位的位置
            a = a ^ b;
            b = temp;
        }
        //如果不存在进位了,返回结果
        return a;
    }

12.最大单词长度乘积(力扣318)

//1.位运算
    public static int maxProduct(String[] words) {

        int len = words.length;
        int[] arr = new int[len];
        for (int i = 0;i < len;i ++){
            for (char c : words[i].toCharArray()){
                /*
                arr[i]记录的是第i个字符串中26个字母是否出现
                abc     ->       000111
                def     ->       111000
                如果相与为0,则两个字符串中没有相同的字母
                * */
                arr[i] |= 1 << (c - 'a');
            }
        }

        int max = 0;
        for (int i = 0; i < len - 1; i++) {
            for (int j = i + 1; j < len; j++) {
                if ((arr[i] & arr[j]) == 0){
                    max = Math.max(max,words[i].length() * words[j].length());
                }
            }
        }
//        for(int i = 0;i < len;i ++){
//            for(int j = 0;j < i;j ++){
//                if ((arr[i] & arr[j]) == 0){
//                    max = Math.max(max,words[i].length() * words[j].length());
//                }
//            }
//        }


        return max;
    }

13.比特位计数(力扣338)

//1.分别计算每个数的位为1的个数,再加和,时间复杂度为O(nlogn)
    public int[] countBits(int n) {
        int[] ans = new int[n + 1];
        for (int i = 0; i <= n; i++) {
            ans[i] = countOne(i);
        }
        return ans;
    }

    public int countOne(int n) {
        int res = 0;
        while (n > 0) {
            if ((n & 1) == 1) {
                res++;
            }
            n >>= 1;
        }
        return res;
    }
//2.分奇数偶数,时间复杂度为O(n)
    /*
     * 偶数的二进制1的个数超级简单,因为偶数是相当于被某个更小的数乘2,
     * 乘2怎么来的?在二进制运算中,就是左移一位,也就是在低位多加1
     * 个0,那样就说明dp[i] = dp[i / 2]
     * 奇数稍微难想到一点,奇数由不大于该数的偶数+1得到,偶数+1在二
     * 进制位上会发生什么?会在低位多加1个1,那样就说明dp[i] = dp[i-1] + 1,
     * */
    public int[] countBits2(int n) {
        int[] dp = new int[n + 1];
        for (int i = 0; i <= n; i++) {
            if (i % 2 == 0) {
                dp[i] = dp[i / 2];
            } else {
                dp[i] = dp[i - 1] + 1;
            }
        }
        return dp;
    }
//3.位运算
    /*
     * i&(i-1):此值必定比i(i>0)小
     * 1.如果i的最低位为1,i&(i-1)相当于去掉i最低位上的1,如i=11,i&(i-1) = 10
     * 2.如果i的最低位不为1,i&(i-1)相当于去掉从最低位向最高位遍历过程中第一个不为0的位上的1
     *  如i=110,i&(i-1) = 100;i=100,i&(i-1)=000;
     * 总的来说:就是清除掉最右侧的1
     *
     * 因为i & (i - 1)的1的个数已经在前面算过了,所以i的1的个数就是 i & (i - 1)的
     * 1的个数加上1
     * */
    public int[] countBits3(int n) {
        int[] res = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            res[i] = res[i & (i - 1)] + 1;
        }
        return res;
    }
//4.位运算
    /*
     * i >> 1会把最低位去掉,因此i >> 1 也是比i小的,同样也是在前面的数组里算过。
     * 1.当 i 的最低位是0,则 i 中1的个数和i >> 1中1的个数相同;
     * 2.当i的最低位是1,i 中1的个数是 i >> 1中1的个数再加1
     * */
    public int[] countBits4(int n) {
        int[] dp = new int[n + 1];
        for (int i = 0; i <= n; i++) {
            dp[i] = dp[i >> 1] + (i & 1);
        }
        return dp;
    }

你可能感兴趣的:(力扣刷题,leetcode,java,数据结构,算法)