[LeetCode] Single Number & Single Number III

前言

Single Number三步曲是LeetCode诸多系列题中流传最广的一个(也许没有之一?),这里放在一起讨论一下。由于Single Number II稍微难一些,所以这篇文章先讨论I和III。

题目

Single Number:https://leetcode.com/problems/single-number/
Single Number ii:https://leetcode.com/problems/single-number-ii/
Single Number iii:https://leetcode.com/problems/single-number-iii/

I

Given an array of integers, every element appears twice except for one. Find that single one.
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

说是有一大堆数字,其中每个数字都会出现两次,但有一个我行我素的,只出现了一次,找出这个数字。

思路

最朴素的思路:统计这些元素的出现,判断元素是否只出现了一次,O(n*n)的复杂度。
或者也可以这样——先把数组排序,然后对比相邻两元素(类似冒泡),通过这种方式发现不同。
但是这道题最流行的还是那个O(n)的异或算法,可以说还是相当巧妙的——

异或(Xor)是位运算的一种,只要这一位两个数字不同,就得到1,否则得到0。需要注意的是:任何数字异或自身都得0;异或满足交换律。设想一组数字,{1,2,3,4,5,4,3,2,1},全部异或,即(1^2^3^4^5^4^3^2^1) = (1^1^2^2^3^3^4^4^5) = 0^5 = 5。
于是这样,就只需要在输入时扫描式的异或处理一遍,即可得到那个只出现一次的数。这种解法实际上属于一种Online Algorithm

代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans = nums[0];
/*      for(auto it = ++nums.begin();it!=nums.end();it++)
        {
            ans = ans ^ (*it);
        }
        return ans;*/
        for(int i = 1;i<nums.size();i++)
        {
            ans = ans ^ nums[i];
        }
        return ans;
    }
};

III

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:
The order of the result is not important. So in the above example, [5, 3] is also correct.
Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?

这次仍然是一大堆数字,每个会出现两次,但其中有俩独行侠,分别只出现一次,请求出这俩数字。

思路

我们从I的异或做法延展开去,想一想,如果对这道题仍然按照全部异或一遍的做法,最终得到的是什么?假设那两个只出现了一次的数字(这两个数字当然是不等的)是a和b,那么我们得到的就是a^b的结果,暂时称之为“X”吧。
由于a与b是不同的数字,它们的位表示也不可能完全相同。 我们规定一个标识位,在这个位置上a和b一个是1,一个是0。(如对a = 5,b = 6,而言,位表示分别为101,110,那么最低位和次低位都可以作为标识位)
现在我们可以将全部数字分成两个阵营,在一个阵营中,这个标识位全部是1,而在另一个阵营中,这个标识位则为0。而我们要寻找的a和b势必不在一个阵营中。对于a所在的阵营,除了a之外的每个数字都会出现两次,对于b的情况,也是如此。
这样一来,这个问题就被转化成了两个Single Number问题,可以用I中的解法再度解决了。

这里还会用到一个tip,为了确保简便,我们不妨把标识位规定为X的位表示中,最右侧的“1”位。实际上,X&=-X就会得到这个位。别忘了-A = ~A+1,另外,通过 (X & (X- 1)) ^ X 的方式也可以得到。

代码

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
    int aXorb = 0;  // the result of a xor b;
    for (auto item : nums) aXorb ^= item;
    //now we get what a^b really is.
    int lastBit = (aXorb & (aXorb - 1)) ^ aXorb;  
    // the last bit that a diffs b
    int intA = 0, intB = 0;
    for (auto item : nums) {
        // based on the last bit, group the items into groupA(include a) and groupB
        if (item & lastBit) intA = intA ^ item;
        else intB = intB ^ item;
    }

    return vector<int>{intA, intB};   
}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值