位运算
  2iwnlIICHNbO 2天前 11 0

异或运算

  • 异或运算就是无进位相加

  • 异或运算满足交换律、结合律

  • 0^n = n,n^n = 0

  • 整体异或和为 x,整体中某个部分的异或和为 y,那么剩下的部分的异或和为 x^y。即若 a^b = c,则 a = c^b,b = c^a

交换两个数

#include <iostream>
#include <vector>

using namespace std;

// i == j 时会出错
void mySwap(vector<int> &arr, int i, int j) {
    arr[i] = arr[i] ^ arr[j];
    arr[j] = arr[i] ^ arr[j];
    arr[i] = arr[i] ^ arr[j];
}

int main() {
    // 两个变量必须有独立的内存空间
    int a = 211;
    int b = -985;
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    cout << a << " " << b << endl;

    vector<int> arr{1, 2, 3};
    mySwap(arr, 0, 1);
    cout << arr[0] << arr[1] << arr[2] << endl;
    // 同一个内存空间,会变成 0
    mySwap(arr, 2, 2);
    cout << arr[0] << arr[1] << arr[2] << endl;
}

返回最大值

package org.example;

public class Main {
    // 必须保证 n 一定是 0 或者 1
    // 0 变 1,1 变 0
    public static int flip(int n) {
        return n ^ 1;
    }

    // 非负数返回 1
    // 负数返回 0
    public static int sign(int n) {
        // 无符号右移
        return flip(n >>> 31);
    }

    // 有溢出风险的实现
    public static int getMax1(int a, int b) {
        int c = a - b;
        // c非负,returnA -> 1
        // c非负,returnB -> 0
        // c负数,returnA -> 0
        // c负数,returnB -> 1
        int returnA = sign(c);
        int returnB = flip(returnA);
        return a * returnA + b * returnB;
    }

    // 没有任何问题的实现
    public static int getMax2(int a, int b) {
        // c 可能是溢出的
        int c = a - b;
        // a 的符号
        int sa = sign(a);
        // b 的符号
        int sb = sign(b);
        // c 的符号
        int sc = sign(c);
        // 判断 A 和 B,符号是不是不一样,如果不一样 diffAB = 1,如果一样 diffAB = 0
        int diffAB = sa ^ sb;
        // 判断 A 和 B,符号是不是一样,如果一样 sameAB = 1,如果不一样 sameAB = 0
        int sameAB = flip(diffAB);
        int returnA = diffAB * sa + sameAB * sc;
        int returnB = flip(returnA);
        return a * returnA + b * returnB;
    }

    public static void main(String[] args) {
        int a = Integer.MIN_VALUE;
        int b = Integer.MAX_VALUE;
        // getMax1 方法会错误,因为溢出
        System.out.println(getMax1(a, b));
        // getMax2 方法永远正确
        System.out.println(getMax2(a, b));
    }
}

136. 只出现一次的数字

#include <vector>

using namespace std;

class Solution {
public:
    int singleNumber(vector<int> &nums) {
        int XOR = 0;
        for (const auto &item: nums)
            XOR ^= item;
        // 最终出现在异或运算中两次的数字会抵消变成 0,最后单独出现的那个就是丢失的,因为没人和他抵消
        return XOR;
    }
};

268. 丢失的数字

#include <vector>

using namespace std;

class Solution {
public:
    int missingNumber(vector<int> &nums) {
        int n = nums.size();
        int XOR = 0;
        for (int i = 0; i < n; ++i) {
            // 这句保证了[0,n]中除了丢失的那个数全都参与异或运算
            XOR ^= nums[i];
            // 这句和结尾的 return 保证了[0,n]全都参与异或运算
            XOR ^= i;
        }
        return XOR ^ n;
        // 最终出现在异或运算中两次的数字会抵消变成 0,最后单独出现的那个就是丢失的,因为没人和他抵消
    }
};

260. 只出现一次的数字 III

  • Brian Kernighan 算法:n & (~n + 1) 提取出整数n最后一位为1的数,等价于 n & (-n)
#include <iostream>
#include <vector>

using namespace std;

class Solution {
public:
    vector<int> singleNumber(vector<int> &nums) {
        int xorSum = 0;
        for (const auto &num: nums)
            xorSum ^= num;
        // 此时 xorSum == a ^ b,xorSum 的二进制中,在 k 位置为 1,说明在 a 和 b 中的 k 位置是相反的
        // 防止溢出
        int rightOne = (xorSum == INT_MIN) ? xorSum : (xorSum & (~xorSum + 1));
        // int rightOne = (xorSum == INT_MIN) ? xorSum : (xorSum & (-xorSum));

        int xorSum2 = 0;
        for (const auto &num: nums) {
            // 只异或上 k 位置为 0 的数,最终结果就是 a 和 b 中的一个
            if ((num & rightOne) == 0)
                xorSum2 ^= num;
        }
        return vector<int>{xorSum2, xorSum ^ xorSum2};
    }
};

137. 只出现一次的数字 II

#include <iostream>
#include <vector>

using namespace std;

class Solution {
public:
    // 已知数组中只有 1 种数出现次数少于 m 次,其他数都出现了 m 次
    // 返回出现次数小于 m 次的那种数
    int find(vector<int> &nums, int m) {
        vector<int> counts(32, 0);
        // 统计每一位上 1 出现的次数
        for (const auto &num: nums)
            for (int i = 0; i < 32; ++i)
                counts[i] += (num >> i) & 1;

        int res = 0;
        for (int i = 0; i < 32; ++i)
            // 查看余数不是 0 的位置
            if (counts[i] % m != 0)
                res |= (1 << i);
        return res;
    }

    int singleNumber(vector<int> &nums) {
        return find(nums, 3);
    }
};
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2天前 0

暂无评论

推荐阅读
  l3iKMwY9XZmO   13天前   34   0   0 算法与数据结构
  cA1FqmrigEPj   16天前   42   0   0 算法与数据结构