位运算部分题解汇总

位运算题解部分汇总

  1. 题目描述:实现一个方法,判断一个正整数是否是2的乘方(比如16是2的4次方,返回True;18不是2的乘方,返回False)。要求性能尽可能高。
  • a. 我们可以用一个循环从1 开始去与目标整数number进行比较,如果相等则返回true,说明该整数是2 的乘方;反之,如果不相等对temp乘以2,然后继续循环。
    如果目标正整数是N,则该算法的时间复杂度为o(logN)。

public static boolean isPowerOf2(int number){
		int temp = 1;
		while(temp <= number){
			if(temp == number)
				return true;
			temp *= 2;
		}
		return false;
	}
  • b.但我们有没有复杂度为O(1)的算法去解决这个问题呢?当然是有的了。我们把2及是2的乘方的正整数的二进制数写出来看看有什么规律:
number二进制数(number -1) 的二进制数
2101
410011
81000111
16100001111

我们会发现只有2的乘方的数的最高位是1,其他位都是0;而给它减1之后,它的二进制的每一位都是1;我们正是可以利用这个用了一下代码。

public static boolean isPowerOf2_2(Integer number){
		if(n <= 0) return false;//要注意这里当number==0的时候
		return (number & (number - 1)) == 0;
	}

2.4的幂

    public boolean isPowerOfFour(int num) {
        if(num <= 0 || (num & (num - 1)) != 0) return false;//4的幂一定是2的幂
        int count = 0;
        while((num >>= 1) != 0){//四的幂的二进制表示数的0的个数是偶数
            count++;
        }
        return count % 2 == 0;
    }

3.汉明距离:两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给出两个整数 x 和 y,计算它们之间的汉明距离。

  • 因为需要计算对应二进制位不同的位置的数目。我们对两个数进行异或运算,发现不同位置得到的结果为1,这样我们就直接计算异或之后的结果二进制位为1的数目。
public int hammingDistance(int x, int y) {
        int temp = x ^ y;
        int count = 0;
        while(temp != 0){
            if((temp & 1) == 1)//判断二进制最后一位是不是为1
                count++;
            temp = temp >> 1;
        }
        return count;
    }

  • 当然我们可以直接使用java中Integer中的bitCount()。
public int hammingDistance(int x, int y) {
        return Integer.bitCount(x ^ y);
    }
  • 位操作的小技巧:在二进制表示中,将 n 和 n - 1与运算总是能把 n 中最低位的 1 变成 0,并保持其他位不变。这个小技巧使代码变得非常简单。
    public int hammingWeight(int n) {
        int count = 0;
        while(n != 0){
            count++;
            n = n & (n - 1);
        }
        return count;
    }

4.数字的补数:给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。
注意:
给定的整数保证在32位带符号整数的范围内。
你可以假定二进制数不包含前导零位。
示例 1:
输入: 5
输出: 2
解释: 5的二进制表示为101(没有前导零位),其补数为010。所以你需要输出2。
链接:https://leetcode-cn.com/problems/number-complement

  • 可以发现对给定正整数的二进制的相应位上对1进行异或运算,就可以得到答案
    public int findComplement(int num) {
        int temp = 1;
        while(temp < num){//计算出与num位数相同(这里的小于号)且各个位为1的数
            temp = temp << 1;
            temp += 1;
        }
        return num ^ temp;
    }

5.二进制表示中质数个计算置位:给定两个整数 L 和 R ,找到闭区间 [L, R] 范围内,计算置位位数为质数的整数个数。
(注意,计算置位代表二进制表示中1的个数。例如 21 的二进制表示 10101 有 3 个计算置位。还有,1 不是质数。)
注意:
L, R 是 L <= R 且在 [1, 10^6] 中的整数。
R - L 的最大值为 10000。
链接:https://leetcode-cn.com/problems/prime-number-of-set-bits-in-binary-representation

  • 一般正常思路
    public int countPrimeSetBits(int L, int R) {
        int count = 0;
        for(int i = L; i <= R; i++){
           int c = Integer.bitCount(i);
            // int num = i;
            // while(num != 0){
            //     if((num & 1) == 1)
            //         c++;
            //     num = num >> 1;
            // }
            if(isPrime(c))
                count++;
        }
        return count;
        
    }
  • 这里可以对isPrime()方法优化下。因为L,R最大为10^6,转换为二进制,有20位,故计算置位个数不会超过20。即求出20以内的质数列表即可。

6.颠倒二进制位:颠倒给定的 32 位无符号整数的二进制位。
示例 1:
输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。
链接:https://leetcode-cn.com/problems/reverse-bits

  • 通过&1得到二进制表示中最低位为0还是1,然后加在temp上,因为存在n的前置0,需要补在temp后面,所以直接用32去控制循环次数。
    public int reverseBits(int n) {
        int temp = 0;
        int c = 32;
        while(c-- > 0){
            temp = temp << 1;
            temp = temp + (n & 1);
            n = n >> 1;
        }
        return temp;
    }
}

7.求众数:给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。

  • 可以在O(n)时间复杂度,O(1)空间复杂度的性能下解决。
  • 题解

8.交替位二进制数:给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。
示例 1:
输入: 5
输出: True
解释:
5的二进制数是: 101
示例 2:
输入: 7
输出: False
解释:
7的二进制数是: 111

  • 对于的正整数,交替位二进制数是一定的,每一个数的二进制表示去掉它的前置0,第一位都为零;那么可以求出与n有相同个二进制位且是交替位二进制数,与n直接比较就ok;一个整数用32位表示,所以while最大循环32次,所以该算法的时间复杂度也是O(1)。
    public boolean hasAlternatingBits(int n) {
        int temp = n, c = 0;
        boolean f = true;
        while(temp > 0){
            temp >>= 1;
            if(f){
                c = (c << 1) + 1;
                f = !f;
            }else{
                c = (c << 1);
                f = !f;
            }
            
        }
        return c == n;
    }
  • 假设n是交替位二进制数,那么n ^ (n >> 1)得到的结果的二进制表示各个位都为1。这就可以做下面的判断了。
public boolean hasAlternatingBits(int n) {//n非零
        int temp = n ^ (n >> 1);
        return (temp & (temp + 1)) == 0; 

        // return (n & (n >> 1)) == 0;//左移的那位,不能知道他的相邻情况
    }

9.字母大小写全排列:给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。
示例:
输入: S = “a1b2”
输出: [“a1b2”, “a1B2”, “A1b2”, “A1B2”]
输入: S = “3z4”
输出: [“3z4”, “3Z4”]
输入: S = “12345”
输出: [“12345”]
注意:
S 的长度不超过12。
S 仅由数字和字母组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/letter-case-permutation

  • 这道题出现在位运算这里,是因为通过位运算来转换字母的大小写。因为大小写字母相差32,又因为异或是不进位加法,所以大写字母和(1<<5)异或变成变成小写字母,小写字母和(1<<5)异或变成大写字母。然后通过递归排列出所有可能。
    public List<String> letterCasePermutation(String S) {
        List<String> ans = new ArrayList();
        recursive(S.toCharArray(), 0, ans);
        return ans;
    }
    
    public void recursive(char[] ch, int i, List ans){
        if(i == ch.length){
            ans.add(String.valueOf(ch));
            return ;
        }
        recursive(ch, i + 1, ans);
        if(ch[i] < '0' || ch[i] > '9'){
            ch[i] ^= (1 << 5);
            recursive(ch, i + 1, ans);
        }
    }

10.找不同不同解法

  • 其中的几种代码:
//通过异或的方法
    public char findTheDifference(String s, String t) {
        int res = 0;
        for(char c : s.toCharArray())
            res ^= c;
        for(char c : t.toCharArray())
            res ^= c;
        return (char)res;
    }
    public char findTheDifference(String s, String t) {

        int a[]=new int[26];
        for(int i=0;i<s.length();i++)
            a[s.charAt(i)-'a']++;
        for(int j=0;j<t.length();j++)
            a[t.charAt(j)-'a']--;
        for(int k=0;k<a.length;k++){
            if(a[k]!=0)
                return (char)(k+'a');
        }
        return 0;
    }

11.缺失数字:给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
官方题解

  • 异或
    public int missingNumber(int[] nums) {
        int res = nums.length;
        for(int i = 0; i < nums.length; i++){
            res ^= i ^ nums[i];
        }
        return res;
    }
    public int missingNumber(int[] nums) {
        //int len = nums.length;//这里可能会造成溢出,做下面的修改
        long len = nums.length;
        int sum = 0;
        for(int num : nums)
            sum += num;
        return len * (1 + len) / 2 - sum;
    }

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

  • a ^ b得到不包括进位的和
    a & b得到哪些是进位
    当a & b不为0时,只要再计算a ^ b与((a & b)<<1)的和即可
    public int getSum(int a, int b) {
        // a ^ b得到不包括进位的和
        // a & b得到哪些是进位
        // 当a & b不为0时,只要再计算a ^ b与((a & b)<<1)的和即可
        if((a & b) == 0)
            return a ^ b;
        return getSum(a ^ b, (a & b) << 1);
    }

题目来源:LeetCode



小结:
  • 对数学公式的运用要敏感。
  • 数学表达式的计算,要注意是否会溢出; 结果溢出、中间结果溢出。
  • 要有通过增加一些东西,是题目变的简单或向简单问题上靠拢。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值