Single Number Ⅰ Ⅱ III【 leetcode解题报告】

11 篇文章 0 订阅
11 篇文章 0 订阅
这篇博客介绍了LeetCode中关于Single Number系列的题目,包括136. Single Number Ⅰ、137. Single Number Ⅱ和260. Single Number III。解题策略主要涉及XOR位运算和哈希映射,旨在找到数组中出现一次的数字。博客详细讲解了位运算的解决方案,包括如何利用位运算在O(n)时间复杂度内解决此类问题,并给出了具体的代码实现。
摘要由CSDN通过智能技术生成

136. Single Number Ⅰ

Description

Given a non-empty array of integers, every element appears twice except for one. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1:

Input: [2,2,1]
Output: 1

Example 2:

Input: [4,1,2,1,2]
Output: 4

思路

  1. 遍历: O(n2) O ( n 2 )
  2. 先排序后遍历: O(nlgn) O ( n l g n )
  3. 利用hashmap:STL用红黑树实现map 搜索的时间复杂度 O(lgn) O ( l g n ) ,遍历的时间复杂度 O(n) , O ( n ) 空间复杂度 O(n) O ( n )
  4. 利用整数在计算机中的二进制bit表示 XOR: O(n) O ( n )

代码

前两种不予以实现。先实现最优方法4:

利用XOR位运算

参考leetcode解题discuss:https://oj.leetcode.com/discuss/6170/my-o-n-solution-using-xor

因为A XOR A = 0,且XOR运算是可交换的,于是,对于实例{2,1,4,5,2,4,1}就会有这样的结果:

(2^1^4^5^2^4^1) => ((2^2)^(1^1)^(4^4)^(5)) => (0^0^0^5) => 5

就把只出现了一次的元素(其余元素均出现两次)给找出来了!

算法复杂度为O(n),且不需要额外空间,代码如下:

//Runtime: 13 ms beats 99.4%
class Solution {
public:
    int singleNumber(vector<int>& nums) {
      //利用异或的性质
        int res=0;
        //此处若改成常规循环for(int i=0;i<nums.size();++i)
        //则用时14ms beats 85.23%
        for(int a:nums){
            res = res^a;
        }
        return res;
    }
};
利用hashmap
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        map<int,int> m;
        for(int i=0;i<nums.size();++i){
            ++m[nums[i]];
        }
        for(auto it:m){
            if(it.second==1){
                return it.first;
            }
        }
        return 0;
    }
};

137. Single Number Ⅱ

Description

Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1:

Input: [2,2,3,2]
Output: 3

Example 2:

Input: [0,1,0,1,0,1,99]
Output: 99

思路

四种思路与上题相同。但第四种方法需要稍加变化。

代码

位运算

只有利用位运算才能实现0额外空的线性复杂度

这道题是除了一个单独的数字之外,数组中其他的数字都出现了三次,利用位操作 Bit Operation 来解此题。我们可以建立一个 32 位的数字,来统计每一位上 1 出现的个数,我们知道如果某一位上为 1 的话,那么如果该整数出现了三次,对 3 去余为 0,我们把每个数的对应位都加起来对 3 取余,最终剩下来的那个数就是单独的数字。代码如下:

//Runtime: 12 ms, beats 67.45%
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res=0;
        for(int i=0;i<32;++i){
            int sum=0;
            for(int n:nums){
                sum += (n >> i) & 1;            
            }
            res |=  (sum % 3) << i;
        }
        return res;
    }
};

根据上面解法的思路,我们把数组中数字的每一位累加起来对 3 取余,剩下的结果就是那个单独数组该位上的数字,由于我们累加的过程都要对 3 取余,那么每一位上累加的过程就是 0->1->2->0,换成二进制的表示为 00->01->10->00,那么我们可以写出对应关系:

00 (+) 1 = 01

01 (+) 1 = 10

10 (+) 1 = 00 ( mod 3)

那么我们用 ab 来表示开始的状态,对于加 1 操作后,得到的新状态的 ab 的算法如下:

b = b xor r & ~a;

a = a xor r & ~b;

我们这里的 ab 就是上面的三种状态 00,01,10 的十位和各位,刚开始的时候,a 和 b 都是 0,当此时遇到数字 1 的时候,b 更新为 1,a 更新为 0,就是 01 的状态;再次遇到 1 的时候,b 更新为 0,a 更新为 1,就是 10 的状态;再次遇到 1 的时候,b 更新为 0,a 更新为 0,就是 00 的状态,相当于重置了;最后的结果保存在 b 中。明白了上面的分析过程,就能写出代码如下:

//Runtime: 10 ms, beats 98.76%
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a = 0, b = 0;
        for (int i = 0; i < nums.size(); ++i) {
            b = (b ^ nums[i]) & ~a;
            a = (a ^ nums[i]) & ~b;
        }
        return b;
    }
};

参考博文:http://www.cnblogs.com/grandyang/p/4263927.html

hashmap

//Runtime: 16 ms, beats 20.05%
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        map<int,int> m;
        for(int i=0;i<nums.size();++i){
            ++m[nums[i]];
        }
        for(auto it:m){
            if(it.second != 3){
                return it.first;
            }
        }
        return -1;
    }
};

260. Single Number III

Description

Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.

For example:

Given nums = [1, 2, 1, 3, 2, 5], return [3, 5].

Note:

  1. The order of the result is not important. So in the above example, [5, 3] is also correct.
  2. Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
思路

这道题其实是很巧妙的利用了 Single Number 单独的数字的解法,因为那道解法是可以准确的找出只出现了一次的数字,但前提是其他数字必须出现两次才行。而这题有两个数字都只出现了一次,那么我们如果能想办法把原数组分为两个小数组,不相同的两个数字分别在两个小数组中,这样分别调用 Single Number 单独的数字的解法就可以得到答案。

那么如何实现呢?首先我们先把原数组全部异或起来,那么我们会得到一个数字,这个数字是两个不相同的数字异或的结果,我们取出其中任意一位为‘1’的位,为了方便起见,我们用 a &= -a 来取出最右端为‘1’的位,然后和原数组中的数字挨个相与,那么我们要求的两个不同的数字就被分到了两个小组中,分别将两个小组中的数字都异或起来,就可以得到最终结果了,参见代码如下:

代码
//Runtime: 14 ms, beats 86.72%
class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int diff = accumulate(nums.begin(), nums.end(), 0, bit_xor<int>());
        diff &= -diff;
        vector<int> res(2, 0);
        for (auto &a : nums) {
            if (a & diff) res[0] ^= a;
            else res[1] ^= a;
        }
        return res;
    }
};

参考博客:http://www.cnblogs.com/grandyang/p/4741122.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值