数据结构和算法笔记(二):位运算

位运算

位异或

  1. 异或的性质:两个数字异或的结果a^b是将 a 和 b 的二进制每一位进行运算,得出的数字。 运算的逻辑是如果同一位的数字相同则为 0,不同则为 1。
  2. 异或的规律:(1)任何数和本身异或则为0;(2)任何数和 0 异或是本身;(3)异或满足交换律。 即 a ^ b ^ c ,等价于 a ^ c ^ b。

只出现一次的数字(其余都出现两次) -> (1)可以用unordered_map记录数组元素出现的次数;(2)更快的方式是直接对所有元素进行异或操作,由于其他元素都出现两次(任何数和本身异或则为0),而且异或满足交换律,所以其他元素异或的结果应该为0,最后结果为这个只出现一次的数。

    int singleNumber(vector<int>& nums) {
        int k = 0; // 性质2,任何数和0异或是本身,所以初始值取0
        for(int i = 0; i < nums.size(); i++){
            k^=nums[i];
        }
        return k;
    }

找到数组中两个只出现一次的数(其余都出现两次)

    vector<int> singleNumbers(vector<int>& nums) {
        int k = 0;
        for(int num:nums){
            k^=num;
        }
        // 假设这两个数字为a和b,此时k=a^b
        // 将nums里的数字分两组,目标是a和b在不同的组里
        int pos = 0, a = 0, b = 0;
        for(int i = 0; i < 32;i++){
            if(k & 1 == 1){ // 找到k中为1的位,说明a和b在该位是不同的
                pos = i;
                break;
            }
            k = k >> 1;
        }
        // 根据找到的pos进行分组,a和b在不同组
        for(int num:nums){
            if((num >> pos) & 1 == 1){
                a^=num;
            }else{
                b^=num;
            }
        }
        vector<int> v{a,b};
        return v;
    }

找到数组中只出现一次的数(其余都出现三次) -> (1) 哈希表存储和查找,使用unordered_map;(2) 如果某个数字出现3次,那么这个3个数字的和肯定能被3整除,则其对应二进制位的每一位的和也能被3整除;统计数组中每个数字的二进制中每一位的和,判断该和是否能被3整除。若可以,则只出现一次的数字的二进制数中那一位为0,否则为1。参考

    int singleNumber(vector<int>& nums) {
        int k = 0;
        for(int i = 0; i < 32; i++){
            int count = 0;
            for(int num:nums){
                if((num>>i)&1 == 1){
                    count++;
                }
            }
            // 出现三次的所有数字的每一位上的和都能被3整除,如果count%3==1表明这个只出现一次的数在这一位为1,否则为0
            if(count%3==1){ 
                k += 1<<i;
            }
        }
        return k;
    }

相关练习

二进制中1的个数
不用加减乘除做加法 -> 位异或操作可以模拟二进制无进位的加法,位与操作可以模拟二进制的进位(位与的结果需要左移1位)。a可以当做无进位的和,b当做进位,然后不断重复之前的操作,直到进位b为0,a便是和。

    int add(int a, int b) {
        int sum, carry;
        while(b!=0){
            sum = a^b;
            carry = (unsigned int)(a&b) << 1;  // C++负数无法左移,需要转换成无符号整型
            a = sum;
            b = carry;
        }
        return a;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章