力扣剑指56:数组中数字出现的次数C++位运算之触类旁通

Posted The Gao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣剑指56:数组中数字出现的次数C++位运算之触类旁通相关的知识,希望对你有一定的参考价值。

剑指56-I

题目重现

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

分析

如果对空间复杂度没有要求的话,开辟一个unordered_map是一个很好的选择。这样的话解决思路就是,遍历两次,第一次遍历目的是填充unordered_map的key值和value值,第二次遍历目的是查找哈希表中value=2的key值。

在此题中,对时间复杂度的要求是O(n),因此不能进行嵌套循环。对空间复杂度的要求是O(1),因此不能开辟额外的数组、vector容器及哈希表等存储空间,只能申请结点或者变量。

在这样的条件下,考虑使用位运算。首先复习一下位运算相关知识。在这里插入图片描述
在本题中,nums中的数字可以分为两类,一类是出现次数为2的数字,一类是出现次数为1的数字。我们知道的结论是,如果一个数字与它本身作异或运算,则相当于把这个数字置0,因为该数字的每一位都与本身是一样的,所以每一位在异或运算后,都变成了0。

如果本题中,只有出现次数为1的数字只有一个,那么就设置一个变量,初始值为0【必须是0,可以思考一下为什么】,对nums中的所有元素都进行按位异或运算。因为上文中我们分析到,每个元素与它本身的元素异或后变成了0,所以所有元素异或的结果其实就是出现次数为1的那个元素。

本题中,出现次数为1的数字有两个,通过上述思路,只能得到这两个数字异或后的结果。因此我们采用分组的思想,分成什么样的两组呢?需要满足以下两点:

出现次数为1的两个数字在不同组;
出现次数为2的一组数字在相同组。

这样的话对两个组分别进行异或运算,就可以得到这两个数字了。那么,如何进行分组才能实现以上的效果呢?

现在我们通过一个变量res记录了两个出现次数为1的数字的异或结果,我们想一下,这个res从右往左第一个1是如何出现的?因为异或运算法则是对于某一位来说相同则0,不同则1,因此我们只要找到res中从右往左第一个1,这就是两个次数为1的数字的区别。

这样的分组依据能保证上述第二点吗?当然可以了。因为相同的数字,每一位也是相同的,一定会分到同一组。

代码

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int res=0,a=0,b=0;
        vector<int>ans;
        for(auto i:nums){
            res^=i;    //得到两个出现次数为1的数字的异或结果
        }
        int div=1;
        while((div&res)==0) div<<=1;  //得到res中第一个非0的数位
        for(auto i:nums){  //按从右到左第一个非0数位分组
            if(div&i) a^=i;
            else b^=i;
        }
        ans.push_back(a);
        ans.push_back(b);
        return ans;
    }
};

剑指56-II

题目重现

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

分析

本题中,没有对时间复杂度和空间复杂度有要求,因此我们可以采用哈希表的方法来做。

开辟一个unordered_map,遍历两次,第一次遍历目的是填充unordered_map的key值和value值,第二次遍历目的是查找哈希表中value=1的key值。

代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_map<int,int>mp;
        for(auto i:nums){
            mp[i]++;    //填充unordered_map
        }
        unordered_map<int,int>::iterator iter=mp.begin();
        for(;iter!=mp.end();iter++){
            if(iter->second==1)break;    //二次遍历得到结果
        }
        return iter->first;
    }
};

以上是关于力扣剑指56:数组中数字出现的次数C++位运算之触类旁通的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer56 - I. 数组中数字出现的次数(位运算)

剑指offer位运算56 - I. 数组中数字出现的次数

剑指offer位运算56 - I. 数组中数字出现的次数

剑指offer位运算56 - II. 数组中数字出现的次数 II

剑指 Offer 56 - I. 数组中数字出现的次数(位运算(异或与),Java)

剑指 Offer 56 - I. 数组中数字出现的次数(位运算(异或与),Java)