[LeetCode] Single Number & Single Number III

来源:互联网 发布:ios10下载bt软件 编辑:程序博客网 时间:2024/06/02 21:20

前言

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