leetcode-singleNum2 找出一个数组中只出现过一次的元素

转自我的搜狐博客:http://jluhlh.blog.sohu.com/302184333.html


leetcode上的题: 所有题都要求线性时间复杂度(最好O(n)),及尽量少的线性空间复杂度(最好O(1))

原题Single Number1: 一个整数数组中有n个元素,其中只有一个只出现一次,其他恰好都出现两次,求出现一次的那个元素;  这个很简单, 用所有元素异或操作,最后的值就是所要的只出现一次的元素。
   int singleNumber(int A[], int n) {
        for(int i=1;i<n;i++)
		A[0]=A[0]^A[i];    
        return A[0];
    }

原题Single Number 2: 一个整数数组中有n个元素,其中只有一个只出现一次,其他恰好都出现三次,求出现一次的那个元素;
出现三次,异或就没意义了,左思右想没有好的答案。(当然,可以先排序,在遍历,时间复杂度O(nlogn))
discuss,第一种方法:比特位的方法,假设整数最大32位,那么所有元素中,对于第j位为1的个数要不是3的倍数(包括0),要不就是3的倍数+1,"+1"就是那个出现一次的元素贡献的,所以,对32位,求出所有元素在这位%3后为1的个数。时间复杂度 k*n,k为整数位数。
int singleNumber(int A[], int n) {
    int count[32] = {0};
    int result = 0;
    for (int i = 0; i < 32; i++) {
        for (int j = 0; j < n; j++)
            if ((A[j] >> i) & 1)
                count[i]++;
        result |= ((count[i] % 3) << i);
    }
    return result;
}
有评论的人提出可以不用count数组,改用一个变量代替,降低空间复杂度。可行!

第二种方法,一种没看懂,说是用三个掩码变量分别表示第j位出现的次数?  掩码是什么? 突然想不起来,
百度后,“掩码是一串二进制代码对目标字段进行位与运算,屏蔽当前的输入位。” 例如IP掩码255.255.255.0。

    int singleNumber(int A[], int n) {
        int ones = 0, twos = 0, threes = 0;
        for(int i = 0; i < n; i++)
        {
            threes = twos & A[i]; //已经出现两次并且再次出现
            twos = twos | ones & A[i]; //曾经出现两次的或者曾经出现一次但是再次出现的
            ones = ones | A[i]; //出现一次的
            
            twos = twos & ~threes; //当某一位出现三次后,我们就从出现两次中消除该位
            ones = ones & ~threes; //当某一位出现三次后,我们就从出现一次中消除该位
        }
        return ones; //twos, threes最终都为0.ones是只出现一次的数
    }
详细解释

另一个人又提出另一个易于理解也易于扩展的版本:(java)
public class Solution {
    public int singleNumber(int[] A) {
        if (A == null) return 0;
        int x0 = ~0, x1 = 0, x2 = 0, t;
        for (int i = 0; i < A.length; i++) {
            t = x2;
            x2 = (x1 & A[i]) | (x2 & ~A[i]);
            x1 = (x0 & A[i]) | (x1 & ~A[i]);
            x0 = (t & A[i]) | (x0 & ~A[i]);
        }
        return x1;
    }
}
并由此推导出更一般化的变形问题:除了一个元素出现L次,其他元素都出现K次,找出出现L次的那个元素,
K元素的数组X[],x[j]表示比特位出现j次,对数组中每个元素a计算 x[j] = (x[j-1] & a) | (x[j] & ~a); 但 x[0] = (x[k] & a) | (x[0] & ~a)。
In the equation, the first part indicates the the carries from previous one.The second part indicates the bits not carried to next one.” ???没看明白
public class Solution {
    public int singleNumber(int[] A, int k, int l) {
        if (A == null) return 0;
        int t;
        int[] x = new int[k];
        x[0] = ~0;
        for (int i = 0; i < A.length; i++) {
            t = x[k-1];
            for (int j = k-1; j > 0; j--) {
                x[j] = (x[j-1] & A[i]) | (x[j] & ~A[i]);
            }
            x[0] = (t & A[i]) | (x[0] & ~A[i]);
        }
        return x[l];
    }
}
 第二种方法的其他文章: 文章1 文章2

综合思考:
如果K为偶数,L为奇数,那么用异或就可很快解决,但如果K为奇数,那就只能用第二题的方法了!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值