Leetcode 题解 - 位运算

Leetcode-461. 汉明距离

两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。

给出两个整数 x 和 y,计算它们之间的汉明距离。

注意:
0 ≤ x, y < 231.

示例:

输入: x = 1, y = 4

输出: 2

解释:
1   (0 0 0 1)
4   (0 1 0 0)
       ↑   ↑

上面的箭头指出了对应二进制位不同的位置。

解法:

  • Java
  1. 异或,相同为0,不同为1,不断右移数1的数量
class Solution {
    public int hammingDistance(int x, int y) {
        int i = x ^ y, cnt = 0;
        while (i != 0) {
            if ((i&1)==1) cnt++;
            i=i>>1;
        }
        return cnt;
    }
}
  • Java
  1. 使用Integer内置函数bitCount
class Solution {
    public int hammingDistance(int x, int y) {
        return Integer.bitCount(x^y);
    }
}
  • Java
    使用 z&(z-1) 去除 z 中的最后的一个1。
class Solution {
    public int hammingDistance(int x, int y) {
        int z = x ^ y, cnt = 0;
        while (z != 0) {
            z = z&(z-1);
            cnt++;
        }
        return cnt;
    }
}

Leetcode-136. 只出现一次的数字

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

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

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

解法:

  • Java
    两个相同的数异或的结果为 0,对所有数进行异或操作,最后的结果就是单独出现的那个数。
class Solution {
    public int singleNumber(int[] nums) {
        for (int i=1;i<nums.length;i++) {
            nums[i] = nums[i-1] ^ nums[i];
        }
        return nums[nums.length-1];
    }
}

Leetcode-268. 缺失数字

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

示例 1:

输入: [3,0,1]
输出: 2
示例 2:

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

说明:

  • 你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现?

解法:

  • Java
  1. 数学方法
class Solution {
    public int missingNumber(int[] nums) {
        int sum = (1+nums.length) * nums.length / 2;
        for (int i=0;i<nums.length;i++) {
            sum -= nums[i];
        }
        return sum;
    }
}
  • Java
    同样使用异或,与索引进行异或
class Solution {
    public int missingNumber(int[] nums) {
        int res = 0;
        for (int i=0;i<nums.length;i++) {
            res = res ^ nums[i] ^ i;
        }
        return res ^ nums.length;
    }
}

Leetcode-260. 只出现一次的数字 III

数组中不重复的两个元素

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

示例 :

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

结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。
你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

解法:

两个不相等的元素在位级表示上必定至少会有一位存在不同。

将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。

diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
在所有数中,可以将此位为1和0的数字分成两个部分,再分别进行异或,即可分别得到孤立的两个数。

  • Java
class Solution {
    public int[] singleNumber(int[] nums) {
        int diff = 0;
        for (int num :nums) diff = diff ^ num;
        diff = diff & (-diff); // 得到最右边的1的位置的数
        int[] res = new int[2];
        for (int num :nums) {
            if ((num&diff) == 0) res[0] = res[0] ^ num;
            else res[1] = res[1] ^ num;
        }
        return res;
    }
}

Leetcode-190. 颠倒二进制位

翻转一个数的比特位

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

示例 1:

输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
      因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。

示例 2:

输入:11111111111111111111111111111101
输出:10111111111111111111111111111111
解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293,
      因此返回 3221225471 其二进制表示形式为 10101111110010110010011101101001。

提示:

  • 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
  • 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。

进阶:

  • 如果多次调用这个函数,你将如何优化你的算法?

解法:

  • Java

“>>>” — 右移运算符,它表示是将运算符左边的对象向右移动运算符右边指定的数,并且在高位补0,其实右移n位,就相当于除于2的n此方;

int a = 16;int b = 2;
System.out.print(a>>>b);
结果是:4

“>>” — 带符号右移运算符,它表示将运算符左边的运算对象,向右移动运算符右边指定的位数。如果是正数,在高位补零,如果是负数,则在高位补1;

对数字的每一位进行判定1还是0,然后移动到相应的位置上

public class Solution {
    public int reverseBits(int n) {
        int res = 0, cur = 0;
        for (int i=0;i<32;i++) {
            cur = n&1;
            res += cur<<(31-i);
            n = n>>>1;
        }
        return res;
    }
}

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

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

Leetcode-231. 2的幂

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

示例 1:

输入: 1
输出: true
解释: 20 = 1
示例 2:

输入: 16
输出: true
解释: 24 = 16
示例 3:

输入: 218
输出: false

解法:

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

Leetcode-342. 4的幂

给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。

示例 1:

输入: 16
输出: true

示例 2:

输入: 5
输出: false

进阶:

  • 你能不使用循环或者递归来完成本题吗?

解法:

  • Java
    4的倍数写成二进制,都在奇数位有且仅有一个1
class Solution {
    public boolean isPowerOfFour(int num) {
        return num > 0 && (num & (num - 1)) == 0 && (num & 0b01010101010101010101010101010101) != 0;
    }
}

Leetcode-693. 交替位二进制数

给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。

示例 1:

输入: 5
输出: True
解释:
5的二进制数是: 101

示例 2:

输入: 7
输出: False
解释:
7的二进制数是: 111

示例 3:

输入: 11
输出: False
解释:
11的二进制数是: 1011

示例 4:

输入: 10
输出: True
解释:
10的二进制数是: 1010

解法:

  • Java

对于 1010 这种位级表示的数,把它向右移动 1 位得到 101,这两个数每个位都不同,因此异或得到的结果为 1111。

class Solution {
    public boolean hasAlternatingBits(int n) {
        int i = n ^ (n>>1);
        return (i&(i+1))==0;
    }
}

Leetcode-476. 数字的补数

给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。

注意:

  • 给定的整数保证在32位带符号整数的范围内。
  • 你可以假定二进制数不包含前导零位。
    示例 1:
输入: 5
输出: 2
解释: 5的二进制表示为101(没有前导零位),其补数为010。所以你需要输出2。

示例 2:

输入: 1
输出: 0
解释: 1的二进制表示为1(没有前导零位),其补数为0。所以你需要输出0。

解法:

  • Java
    对于 00000101,要求补码可以将它与 00000111 进行异或操作。那么问题就转换为求掩码 00000111。
class Solution {
    public int findComplement(int num) {
        if (num==0) return 1;
        int mask = 1<<30;
        while ((mask&num) == 0) mask = mask>>1;
        mask = (mask<<1) - 1;
        return mask ^ num;
    }
}
  • Java
    java内置函数
public int findComplement(int num) {
    if (num == 0) return 1;
    int mask = Integer.highestOneBit(num);
    mask = (mask << 1) - 1;
    return num ^ mask;
}

Leetcode-371. 两整数之和

不使用运算符 + 和 - ​​​​​​​,计算两整数 ​​​​​​​a 、b ​​​​​​​之和。

示例 1:

输入: a = 1, b = 2
输出: 3

示例 2:

输入: a = -2, b = 3
输出: 1

解法:

  • Java

a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。

递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。

class Solution {
    public int getSum(int a, int b) {
        return b==0?a:getSum(a^b, (a&b)<<1);
    }
}

Leetcode-318. 最大单词长度乘积

给定一个字符串数组 words,找到 length(word[i]) * length(word[j]) 的最大值,并且这两个单词不含有公共字母。你可以认为每个单词只包含小写字母。如果不存在这样的两个单词,返回 0。

示例 1:

输入: ["abcw","baz","foo","bar","xtfn","abcdef"]
输出: 16 
解释: 这两个单词为 "abcw", "xtfn"。

示例 2:

输入: ["a","ab","abc","d","cd","bcd","abcd"]
输出: 4 
解释: 这两个单词为 "ab", "cd"。

示例 3:

输入: ["a","aa","aaa","aaaa"]
输出: 0 
解释: 不存在这样的两个单词。

解法:

  • Java

本题主要问题是判断两个字符串是否含相同字符,由于字符串只含有小写字符,总共 26 位,因此可以用一个 32 位的整数来存储每个字符是否出现过。

class Solution {
    public int maxProduct(String[] words) {
        int n = words.length;
        int[] val = new int[n];
        for (int i=0;i<n;i++) {
            for (char c :words[i].toCharArray()) {
                val[i] = val[i] | (1<<(c-'a'));
            }
        }
        int res = 0;
        for (int i=0;i<n;i++) {
            for (int j=i+1;j<n;j++) {
                if ((val[i] & val[j])==0) {
                    res = Math.max(res, words[i].length() * words[j].length());
                }
            }
        }
        return res;
    }
}

Leetcode-338. 比特位计数

给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

示例 1:

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

示例 2:

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

进阶:

  • 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
  • 要求算法的空间复杂度为O(n)。
  • 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。

解法:

  • Java

动态规划
对于数字 6(110),它可以看成是 4(100) 再加一个 2(10),因此 dp[i] = dp[i&(i-1)] + 1;

class Solution {
    public int[] countBits(int num) {
        int[] res = new int[num+1];
        for (int i=1;i<=num;i++) res[i] = res[i&(i-1)] + 1;
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值