lintCode-落单的数III

闲暇之余逛到了lintcode,便打算做一些题目,做时却发现很多东西已然忘却,为了记住重新思考时的思路,就有了这个博客,水平不高,仅做笔记。

原题:
给出2*n + 2个的数字,除其中两个数字之外其他每个数字均出现两次,找到这两个数字。

看到题目的第一反应就是穷举解法(对于计算机而言,很少有不能用穷举来解决问题的,但实际上存在这个想法并不好,奈何本人算法确实较弱,拿到题目一般反应就是穷举解法,希望以后能有进步)。

穷举解法在这里的思路就是:判断一个数是否单次出现,只需遍历之后的数中有没有在出现即可。一个无关紧要的trick是,用一个数组记录已经匹配的数的index,这样可以不用每次都从头开始比较,也可以略过已经匹配的数。代码如下:

class Solution {
public:
    /*
     * @param A: An integer array
     * @return: An integer array
     */
    vector<int> singleNumberIII(vector<int> &A) {
        // write your code here        
        vector<bool> vecFlag;
        vecFlag.resize(A.size(), 0);
        vector<int> ret;

        for ( int i = 0; i < A.size(); ++i )
        {
            if ( vecFlag[i] )
            {
                continue;
            }
            int j = i + 1;
            for ( ; j < A.size(); ++j )
            {
                if ( vecFlag[j] )
                {
                    continue;
                }
                if ( A[i] == A[j] )
                {
                    break;
                }
            }
            if ( j >= A.size() )
            {
                ret.push_back(A[i]);
                if ( 2 == ret.size() )
                {
                    return ret;
                }
            }
            else
            {
                vecFlag[j] = 1;
            }
        }
        return ret;
    }
};

上诉方法时间复杂度:T = (n-1))+ (n -2)+(n - 3)…. + 1; ⇒ O(N^2), 空间复杂度: O(N)

此段代码在lintCode上提交后,测试用例跑了一部分之后,会有超时的信息。那么此时不妨看一下挑战的要求:
O(n)时间复杂度,O(1)的额外空间复杂度

看到O(1)的额外空间时,我在想这个空间能做什么,于是我画了下面这样的一个草图。
这里写图片描述
对于临时空间而言,在A,B,A 遍历之后,临时空间中得到的数最好是什么,显然为B,那么可以抽象为 A[]B[]A[] = B ,[]中目标操作,什么操作可以去掉相同的数,这是抑或就从我脑海里显现出来了。通过对整个数组进行抑或操作之后,可以得到B^D的值。

剩下的问题是如何将 B和D从 B^D的值中分离出来了。此时我首先想到了将B^D的值与数组进行第二次遍历,第二次遍历过程中肯定会发生如下情况:
B^D^B^D的情况,如果某次抑或之后,值为0,那么此刻就可以得到D, 剩下来B就好确定了,但遗憾的是,实际过程中存在如下的异常情况:
B^D = C, C^C = 0 此时计算的B和D就都是错的了。

思路到这突然就卡住了,一时都怀疑抑或的方法是否可行,但由于暂时想不到其他方法,于是我整理了一下目标,和现有的策略。
目标是要找出两个不同的数,策略是通过抑或去掉相同得数。 整理之后发现如果只有一个不同的数,那么通过抑或不就可以很方便的得到吗。只要把B和D分开在两个数组里就可以解决了。

因 B != D , 所以 B^D != 0, 设 B^D = E, 若E的二进制中第K位不等于0(肯定存在一个k,因为E!= 0),那么B与D的第K bit位肯定不同,根据第K bit位是否为1,将数组array中的数分为array_b和array_d两组,分别对两组数组求抑或就可以计算出B和D了。
此方案的代码如下:

class Solution {
public:
    /*
     * @param A: An integer array
     * @return: An integer array
     */
    vector<int> singleNumberIII(vector<int> &A) {
        // write your code here
        // first round find x^y

        int tmp = 0;
        for ( int i = 0; i < A.size(); i++ )
        {
            tmp ^= A[i];
        }

        // find bit k
        int k = 0;
        while( tmp %2 == 0 )
        {
            k++;
            tmp >>= 1;
        }

        // second round find x y
        int x = 0, y = 0;
        for ( int i = 0; i < A.size(); i++ )
        {
            if ( (A[i] >> k) & 1  )
            {
                x ^= A[i];
            }
            else
            {
                y ^= A[i];
            }
        }

        vector<int>ret;
        ret.push_back(x);
        ret.push_back(y);
        return ret;
    }
};

时间复杂度和空间复杂度均能达到要求
总结:该方案的关键在于使用抑或来消除相同的数,对于经常写算法的人来说可能很容易想到,但对于一个不甚熟悉的人来说,如何想到使用抑或来做才是重要的。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值