剑指OFFER-数组中只出现一次的数字

剑指OFFER-数组中只出现一次的数字

Question

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
关键词:数组 次数 异或

Solution

看到这题就想起那个只出现一次的字符。不过这题已知条件更多一点,至少我们知道会有两个出现一次的数字,而且其他数字成双成对出现。

sort + pop

第一反应当然是排序啦(N·log(N)),然后利用栈,进去相同就弹出,最后剩下俩数字。
时间复杂度:O(N·log(N) + N)
空间复杂度:O(1)

  • Python
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        array.sort()
        ans = []
        for a in array:
            if not ans or a!=ans[-1]:
                ans.append(a)
            elif a == ans[-1]:
                ans.pop()
        return ans
  • C++
class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        vector<int> ans;
        sort(data.begin(), data.end());
        for(int i=0; i<data.size(); i++){
            if(ans.empty() || data[i] != ans.back())
                ans.push_back(data[i]);
            else
                ans.pop_back();
        }
        *num1 = ans[0];
        *num2 = ans[1];
        return;
    }
};

异或

然后神奇的教科书还有一种解法,达到了理论上最小的复杂度(N),那就是利用异或的特性:
交换律:A ^ B = B ^ A 其他:A ^ A = 0, A ^ 0 = A

1)假设数组是[a, c, d, b, d, c],对所有数字异或,根据交换律a ^ c ^ d ^ b ^ d ^ c = a ^ b ^ (c ^ c ^ d ^ d) = a ^ b ^ 0 = a ^ b。

2)现在我们得到一个k = a ^ b,k中如果某位为0代表a和b该位相同,如果为1代表a和b该位不相同。我们找一个k中为1的任何一位作为标志(这里我们用第一个为1的位s),因为a、b在这一位不同,那么他们一个是0,一个是1。我们把s位是1的数字分为一组,剩下是0的作为另外一组。这两组中分别包含a、b以及其他的相同数字。假设第一组是[a, c, c, …],重复第一步方法,将这个数组全部异或,我们可以得到a ^ (c ^ c ^ …) = a,同理b ^ (d ^ d ^ …) = b

时间复杂度:O(N)
空间复杂度:O(1)

  • Python
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        c = 0
        for n in array:
            c = c ^ n
        index = 1
        while index & c == 0:
            index = index << 1
        a, b = 0, 0
        for n in array:
            if index & n == 0:
                a = a ^ n
            else:
                b = b ^ n
        return [a, b]
  • C++
class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        int c = 0;
        for(int i = 0; i < data.size(); i++)
            c = c ^ data[i];
        int index = 1;
        while((index & c) == 0)
            index = index << 1;
        int a= 0, b = 0;
        for(int i = 0; i < data.size(); i++){
            if((index & data[i]) == 0)
                a = a ^ data[i];
            else 
                b = b ^ data[i];
        }
        *num1 = a;
        *num2 = b;
        return;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值