基础算法(5)——位运算

1. 常见位运算总结

1) 基础位运算


2) 给一个数 n,确定它的二进制表示中的第 x 位是 0 还是 1


3) 将一个数 n 的二进制表示的第 x 位修改成 1


4) 将一个数 n 的二进制表示的第 x 位修改成 0


5) 位图的思想

位图的本质就是 哈希表


6) 提取一个数 n 二进制表示中最右侧的 1(lowbit)


7) 干掉一个数 n 二进制表示中最右侧的 1

例题:

191. 位1的个数 - 力扣(LeetCode)

338. 比特位计数 - 力扣(LeetCode)

461. 汉明距离 - 力扣(LeetCode)


8) 位运算的优先级

位运算符太多了,容易记混,所以不记了,能加括号就加括号


9) 异或(^)运算的运算律

例题:

136. 只出现一次的数字 - 力扣(LeetCode)


2. 判定字符是否唯一

题目描述:

解法一:数组模拟哈希表

代码实现:

class Solution {
    public boolean isUnique(String astr) {
        int n = astr.length();
        if (n > 26) return false; //字符串仅包含小写字母
        char[] arr = astr.toCharArray();
        int[] ret = new int[26];
        for (int i = 0; i < n; i++) {
            ret[arr[i] - 'a']++;
            if (ret[arr[i] - 'a'] > 1) {
                return false;
            }
        }
        return true;
    }
}

解法二:位图

代码实现:

class Solution {
    public boolean isUnique(String astr) {
        //鸽巢原理优化
        if (astr.length() > 26) return false;

        int bitMap = 0;
        for (int i = 0; i < astr.length(); i++) {
            int x = astr.charAt(i) - 'a';
            //判断字符是否已存在位图中
            if (((bitMap >> x) & 1) == 1) return false;

            //将当前字符加入到位图
            bitMap |= 1 << x;
        }
        return true;
    }
}

3. 丢失的数字

题目描述:

解法一:哈希表

//哈希表
class Solution {
    public int missingNumber(int[] nums) {
        int n = nums.length;
        int[] hash = new int[n + 1];
        for (int i = 0; i < n; i++) {
            hash[nums[i]]++;
        }
        for (int i = 0; i < hash.length; i++) {
            if (hash[i] == 0) {
                return i;
            }
        }
        return -1;
    }
}

解法二:高斯求和

//高斯求和
class Solution {
    public int missingNumber(int[] nums) {
        int n = nums.length;
        int sum1 = 0;
        int sum2 = 0;
        for (int i = 0; i < n; i++) {
            sum1 += nums[i];
        }
        for (int i = 0; i <= n; i++) {
            sum2 += i;
        }
        return sum2 - sum1;
    }
}

解法三:位运算(异或运算的运算律)

//位运算
class Solution {
    public int missingNumber(int[] nums) {
        int n = nums.length;
        int ret = 0;
        for (int i = 0; i < n; i ++) {
            ret ^= nums[i];
        }
        for (int i = 0; i <= n; i++) {
            ret ^= i;
        }
        return ret;
    }
}

4. 两整数之和

题目描述:

解法:位运算(异或运算 - 无进位相加)

算法思路:

代码实现:

class Solution {
    public int getSum(int a, int b) {
        while (b != 0) {
            int x = a ^ b; // 计算无进位相加的结果
            int y = (a & b) << 1; // 计算进位
            a = x;
            b = y;
        }
        return a;
    }
}

5. 只出现一次的数字 II

题目描述:

解法:利用位运算依次确定每一个二进制位

代码实现:

class Solution {
    public int singleNumber(int[] nums) {
        int ret = 0;
        for (int i = 0; i < 32; i++) { // 依次修改 ret 中的每一个比特位
            int sum = 0;
            for (int x : nums) { // 统计 nums 中所有的数的第 i 位二进制的和
                if (((x >> i) & 1) == 1) {
                    sum++;
                }
            }
            sum %= 3;
            if (sum == 1) ret |= (1 << i); // 将ret 的第 i 位二进制修改为 1
        }
        return ret;
    }
}

6. 只出现一次的数字III

题目描述:

算法思路:

是下面 7 题的思路的一部分

代码实现:

class Solution {
    public int[] singleNumber(int[] nums) {
        // 1. 找到 a ^ b
        int tmp = 0;
        for (int x : nums) {
            tmp ^= x;
        }

        // 2. 找到 tmp 二进制位中最右侧为 1 的位置
        int diff = 0;
        while (true) {
            if (((tmp >> diff) & 1) == 1) {
                break;
            }
            diff++;
        }

        //  3. 按照 diff 位置为1或0,将数组分为两类分别异或
        int[] ret = new int[2];
        for (int x : nums) {
            if (((x >> diff) & 1) == 1) {
                ret[0] ^= x;
            } else {
                ret[1] ^= x;
            }
        }
        return ret;
    }
}

7. 消失的两个数字

题目描述:

算法思路:

结合 268. 丢失的数字 和 260. 只出现一次的数字 III 两题

代码实现:

class Solution {
    public int[] missingTwo(int[] nums) {
        int n = nums.length;

        // 1. 先把所有数异或在一起
        int tmp = nums[0];
        for (int i = 1; i < n; i++) {
            tmp ^= nums[i];
        }
        for (int i = 0; i <= n + 2; i++) { // 注:此处为 <=
            tmp ^= i;
        }

        // 2. 找到 a,b 两个数比特位不同的位置
        int x = -1; // 使用 x 记录该位置
        for (int i = 0; i < 32; i++) {
            if (((tmp >> i) & 1) == 1) {
                x = i;
                break;
            }
        }

        // 3. 将所有的数按照 x 位置是0还是1,分为两类异或
        int[] ret = new int[2];
        for (int i : nums) {
            if (((i >> x) & 1) == 1) ret[0] ^= i;
            else ret[1] ^= i;
        }
        for (int i = 1; i <= n + 2; i++) {
            if (((i >> x) & 1) == 1) ret[0] ^= i;
            else ret[1] ^= i;
        }
        return ret;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值