136. Single Number I-II-III

https://leetcode.com/problems/single-number/

https://leetcode.com/problems/single-number-ii/

https://leetcode.com/problems/single-number-iii/

1. Given an 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?

题目:给定一组整数,每个都出现两次,仅有一个出现一次,要求时间复杂度O(n),不使用额外内存。


思路:考虑到内存限制,挨个比较、计数不允许;不允许使用排序;考虑使用位运算。

相同的数字,按位异或操作,结果为0;异或操作满足交换律,即,与顺序无关。因此C++代码如下:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int length = nums.size();
        if(length<1) return 0;
        if(length<2) return nums[0];
        int res = nums[0];
        for( int i=1; i<length; ++i ){
            res ^= nums[i];
        }
        return res;
    }
};



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

题目:同1,只是数字出现了三次,且仅有一个数字出现一次


思路:参考http://www.cnblogs.com/grandyang/p/4263927.html

方案1,统计每一位上1出现的次数。目标是将出现三次的数字经过一定运算后置为0,而出现一次的数字保持原值。因此对于出现三次的数字而言,0位不用考虑,1位出现3次,则置为0; 以建立一个32位的数字,来统计每一位上1出现的个数,该位统计结果对3取余。代码如下:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for (int i = 0; i < 32; ++i) {
            int sum = 0;
            for (int j = 0; j < nums.size(); ++j) {
                sum += (nums[j] >> i) & 1;
            }
            res |= (sum % 3) << i;
        }
        return res;
    }
};

方案2,用三个整数one, two, three来标识各位累积出现1的次数,若出现3次,则清零。因为是one和当前数字和上次循环的two决定了新的two,上次循环的one和当前数字决定了新的one,本次的one和本次的two决定了本次的three,本次的three确定后,three为1的位置即为出现3次的位置,需要清零,因此对one和two的相应位置置0.代码如下:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int length = nums.size();
        int ones = 0, twos = 0, threes = 0;
        for( int i=0; i<length; ++i){
            twos |= ones&nums[i];
            ones ^= nums[i];
            threes = ones & twos;
            ones &= ~threes;
            twos &= ~threes;
        }
        return ones;
    }
};

3.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?

问题:一组整数,两个数字出现了1次,其他数字都出现了两次,找出这两个数字,返回结果顺序不重要,线性时间复杂度,常数空间复杂度


思路:出现两次的数字,只有按位异或才可抵消,而整体按位异或一遍之后,出现一次的两个数字a,b也进行了异或运算,得到的结果diff为两个数字不同位为1,相同位为0。注意,diff中可能有很多位都是1,如果能找出它仅某一位第k位为1,其他位为0的对应数字diff2,将diff2和原数字序列挨个按位与,若num[i]结果为真,则num[i]参与异或运算序列1,否则参与异或运算序列2,序列1所得结果为a/b中第k位为1的那一个,序列2所得结果为a/b中第k位为0的那一个。

从diff获取diff2的思路如下:

diff2 = diff & (-diff);

diff2保留了diff最右边为1的位为1,其余位为0.理由如下:数字在计算机中是以补码形式存在,对于正数diff,其补码为原码按位取反,末位加1,补码和源码的唯一相同位在于原码最右第一个为1的位置均为1,其他位均不同。

代码如下:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
/*        vector<int> res;
        int length = nums.size();
        if( length<1 ) 
            return res;
        int result = nums[0];
        for( int i=1; i<length; ++i){
            result ^= nums[i];
        }
        res[0] = 0;
        res[1] = 0;
        int n = result & (~(result-1)); 
        for( int i=0; i<length; ++i){
            if((n & nums[i])!=0){
                res[0] = res[0] ^ nums[i]; 
            }else{
                res[1] = res[1] ^ nums[i];
            }
        }
        return res;
*/
        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;
    }
};



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值