【位运算】--- 进阶题目赏析

 Welcome to 9ilk's Code World

       

(๑•́ ₃ •̀๑) 个人主页:        9ilk

(๑•́ ₃ •̀๑) 文章专栏:     算法Journey 


本节我们来赏析位运算的一些进阶题目。


🏠 只出现一次的数字II

📌 题目解析

只出现一次的数字II

📌 算法原理

✏️ 思路一:哈希表+异或

由题目意思可得,除了只出现一次的数字之外,其他数字都出现了三次,我们可以利用哈希表,我们遍历一遍数组,将这些数异或用ret记录,当某个数出现三次时,我们再用ret异或这个数,最后得到的结果就是只出现一次的数字,因为其他数字在我们操作下都出现了四次被异或为0。

参考代码:

class Solution {
public:
    int singleNumber(vector<int>& nums)
    {
        unordered_map<int,int> hash;
        int ret = 0; 
        for(const auto& e : nums)
        {
            hash[e]++;
            ret ^= e;
            if(hash[e] == 3) //出现三次再次异或
              ret ^= e;
        }      
        return ret; 
    }
};


✏️ 思路二: bit位分组

由于其他数字都出现3次,因此我将数组中每个数特定bit位相加,对于出现过三次的这些数,他们特定bit位相加得到的一定是3的倍数,%3得到0,最后数组中所有数加完后再%3得到的一定是只出现一次那个数字上的bit位的数字.因此,我们可以不断向左移,对每个bit位进行这样的操作,就能得到只出现一次数字的完整bit位了.

参考代码:

class Solution {
public:
    int singleNumber(vector<int>& nums)
    { 
        int ret  = 0;
        for(int i = 0 ; i < 32 ; i++)
        {
            int sum = 0;
            for(int x : nums)
            {
                 if((x>>i)&1) sum++;
            }
            sum %= 3;//得到的是只出现一次数字i位上的数字
            if(sum == 1) ret |= (1 <<i);
        }
        return ret; 
    }
};



拓展 : 如果其他数出现n次,另外一个数字出现m次,我们同样可以利用这种方式来找出不同点(%n)从而来找到只出现一次的数字.

🏠 只出现一次的数字III

📌 题目解析

只出现一次的数字III

  • -2^31 <= nums[i] <= 2^31 - 1 注意数据范围过大使用int可能会导致数据溢出。

📌 算法原理

由图中我们可以知道,ret最后一定是两个只出现一次的数字(记为a和b)的异或,由于异或是相同为0,相异为1,因此我们可以利用n&(-n)提取异或后的ret最右边的1,也就是a和b在这个bit位上的数字不同,依照这个不同可以将原数组中的数字划分为两种:一种是k位上是1,一种是k位上是0,由于其他数字出现了两次他们异或之后得到的一定是0,最后ret1和ret0得到的一定是只出现一次的两个数字。

参考代码:

class Solution 
{
public:
   typedef long long ll;
    vector<int> singleNumber(vector<int>& nums)
    {
          ll ret = 0;
       for(int i = 0 ; i < nums.size() ; i++)
       {
           ret ^= nums[i]; //得到的是两个不同的数异或
       }
       //我们先找ret最右边的1因为异或相异为1 这样就可以依照这个把a分到这一位上有0/1,b分到这一位有1/0
       ll k = ret & (-ret);
       //用k判断某一位是1还是0
       ll ret1 = 0;
       ll ret0 = 0;
       for(int i = 0 ; i < nums.size();i++)
       {
          if(nums[i] & k) //说明该位上为1
              ret1 ^= nums[i];
          else
            ret0 ^= nums[i];
       }
 
        return {(int)ret1,(int)ret0};
    }
}s2;

🏠 消失的两个数字

📌 题目解析

消失的两个数字

📌 算法原理

这道题其实就是丢失的数字 + 只出现一次的数字III。我们可以通过nums的数组长度知道未消失前数字的总数。异或一遍原来数字再异或一遍nums,就可以得到消失两个数字的异或。此时就转化为只出现一次的数字III,可以利用ret&(-ret)提取两者不同的bit位,依据不同再遍历一遍进行分组得到这两个数字。

参考代码:

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) 
    {
        int n = nums.size() + 2;
        int ret = 0;
        for(int i = 1 ; i <= n ; i++)
        {
             ret ^= i;
        } 
        for(auto& e : nums)
        {
            ret ^= e;
        }// 1^4
        int k = ret & (-ret); //提取最右边的一个1 消失的两个数在第k位不同
        int ret1 = 0;
        int ret0 = 0;
        for(int i = 1 ; i <= n ; i ++)
        {
              if(i & k)
               ret1 ^= i;
              else
               ret0 ^= i; 
        }
        for(auto& e : nums)
        {
             if(e & k)
               ret1 ^= e;
              else
              ret0  ^= e; 
        }
        int max = 0;
        int min = ret0 > ret1 ? (max = ret0,ret1) : (max = ret1,ret0);
        return {min,max};
    }
};

完。

评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值