LeetCode-136. 只出现一次的数字 超详细思路及过程[E]

在这里插入图片描述

题目描述

136. 只出现一次的数字
难度:简单
相关标签:位运算、数组

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间

运行示例
示例 1 :
输入:nums = [2,2,1]
输出:1
示例 2 :
输入:nums = [4,1,2,1,2]
输出:4
示例 3 :
输入:nums = [1]
输出:1

提示
1 <= nums.length <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
除了某个元素只出现一次以外,其余每个元素均出现两次。

题目分析与实现

看到这道题,我首先想到的是记录每个元素出现的次数,再找出只出现一次的元素。因为题目给出的数组长度永远为奇数,我们可以通过开辟一个长度为nums.size()/2+1的二维数组。
在这里插入图片描述
咱们来看看这个方式实现的算法具体长什么样,我们再来分析分析:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int n = nums.size();
        //创建用于存储各个数字出现次数的二维数组
        vector<vector<int>>newNums;
        for(int i = 0; i < n; i++)
        {
        	//haveSearch用于记录nums[i]是否出现在二维数组中
            int haveSearch = 0;
            for(int j = 0; j < newNums.size(); j++)
            {
                if(nums[i] == newNums[j][0])
                {
                    newNums[j][1]++;
                    haveSearch = 1;
                    break;
                }
            }
            //没在二维数组中找到,则存入nums[i]
            if(haveSearch == 0)
            {
                vector<int>temp(2);
                temp[0] = nums[i];
                temp[1] = 1;
                newNums.push_back(temp);
            }
        }
        int ret = -1;
        //遍历寻找只出现一次的数字
        for(int i = 0; i < newNums.size(); i++)
        {
            if(newNums[i][1] == 1)
            {
                ret = newNums[i][0];
                break;
            }
        }
        return ret;
    }
};

上面的方法开辟了一个二维数组,因而空间复杂度为O(n)。在遍历nums时,每次都要遍历newNums,因而时间复杂度为O(n^2)。这显然效率不高。

首先让我们解决一下时间复杂度的问题。用于存储数字并可快速存取,就会考虑到哈希表。哈希表时间复杂度为O(1)。再来看看哈希表实现的代码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_map<int,int>hashMap;
        int n = nums.size();
        for(int i = 0; i < n; i++)
        {
            auto fRet = hashMap.find(nums[i]);
            if(fRet == hashMap.end())//没找到,则插入nums[i]
            {
                hashMap.insert(pair(nums[i], 1));
            }
            else//找到,则将出现次数+1
            {
                fRet->second++;
            }
        }
        int ret = -1;
        for(auto kv : hashMap)
        {
            if(kv.second == 1)
            {
                ret = kv.first;
                break;
            }
        }
        return ret;
    }
};

上面这种方法仅仅优化了时间复杂度,将时间复杂度将为O(n),但空间复杂度仍为O(n)。类似的方法还有哈希集合结合数学的方法。思路大概是这样的:将nums中出现的元素存储进哈希集合中(重复元素哈希集合只会存储一次),使用sum存储出现在哈希集合中的元素的2倍,再用sum减去nums中的各个元素,剩下的就是只出现一次的元素。
在这里插入图片描述

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        set<int>numsSet;
        int n = nums.size();
        for(int i = 0; i < n; i++)
        {
            numsSet.insert(nums[i]);
        }
        int sum = 0;
        for(int i : numsSet)
        {
            sum += 2 * i;
        }
        for(int i = 0; i < n; i ++)
        {
            sum -= nums[i];
        }
        return sum;
    }
};

当然,上面这三种方法都不满足线性时间复杂度和常数阶空间复杂度的要求。接下来,介绍一下我们的大牛算法——位运算

这一题和我们之间讨论过的面试题 17.04. 消失的数字很像。这里我们再来巩固巩固位运算:

如果两个相同的数字做异或运算,则其结果为0(如下图所示)。
在这里插入图片描述
如果一个数与0做异或运算,则其结果为其本身(如下图所示)。
在这里插入图片描述
利用上面异或的性质。我们以nums = [4,1,2,1,2]为例。我们将storage初始化为0,因为storage若与1异或后得storage为1,在与另一个1异或得到storage为0。同理,storage与两个2异或得到的也是0。最终只会剩下storage与4异或的结果,即4。

因此,我们可以使用一个初始化为0的遍历,将其与nums中的各个元素异或,最终结果将是只出现一次的数字。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int storage = 0;
        int n = nums.size();
        for(int i = 0; i < n; i++)
        {
            storage ^= nums[i];
        }
        return storage;
    }
};

这不就是满足题目要求的算法嘛!线性时间复杂度O(n)和常数阶空间复杂度O(1)。此时我的快乐只能用这个表情形容了👇
在这里插入图片描述

上面就是今天分享的所有内容啦!下一文章见!
在这里插入图片描述
🎇有更优秀的算法,欢迎在评论区讨论!!

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值