剑指offer之数组中只出现一次的数字

题目描述

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

解题思路

方法一:异或

除了有两个数字只出现了一次,其他数字都出现了两次。异或运算中,任何一个数字和自己本身异或都是0,任何一个数字和0异或都是本身。

如果尝试把原数组分成两个子数组,且刚好每个子数组中各自包含一个只出现一次的数字。则在该前提下,每个子数组中,只有一个数字出现了一次,其他数字都出现了两次。

针对每个子数组,从头到尾依次异或每个数字,则最后留下来的就是只出现了一次的数字。因为出现两次的都抵消掉了。

怎样实现子数组的划分呢。若对原数组从头到尾的进行异或,则最后得到的结果就是两个只出现一次的数字的异或运算结果。这个结果的二进制表示中,至少有一位为1,因为这两个数不相同。该位记为从最低位开始计数的第n位。

则分组的标准定为从最低位开始计数的第n位是否为1。因为出现两次的同一个数字,各个位数上都是相同的,所以一定被分到同一个子数组中,且每个子数组中只包含一个出现一次的数字。

例如:数组元素为2,4,3,6,3,2,5,5,异或的结果为0010,也就是倒数第二位为1,也就是倒数第二位为标记位来进行拆分,那么第一个子数组就为{2,3,6,3,2},他们的倒数第二位为1,第二个子数组为{4,5,5}倒数第二位为0,对这两个子数组分别再异或,最终找到6和4。

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        //异或
        if(data.size() < 2) 
            return;
        int temp = data[0];
        for(int i = 1;i < data.size();i++) //先求出全部数的异或结果
            temp ^= data[i];
        if(temp == 0) //异或结果为0,说明没有两个只出现一次的不同的数字
            return;
        int index = 0;  //index为异或结果中1所在的最低位
        while((temp&1)==0){
            temp >>= 1;
            ++index;
        }
        *num1 = *num2 =0;
        for(int i=0;i<data.size();i++){ 
            if((data[i]>>index)&1) //表示每个数在标记index的地方为1
                *num1 ^= data[i];
            else                   //表示每个数在标记index的地方为0
                *num2 ^= data[i];
        }
    }
};

方法二:哈希表

用STL中的unorder_map存储,key为数组中每个数,value为其出现的次数,最后出现次数为1的,就是结果

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        //哈希表
        unordered_map<int, int> map;
        for(int i = 0; i < data.size(); i++)
            map[data[i]]++;
        vector<int> v;
        for(int i = 0; i < data.size(); i++)
            if(map[data[i]]== 1)
                v.push_back(data[i]);
        *num1 = v[0]; 
        *num2 = v[1];
    }
};

方法三:

遍历数组,使用一个ArrayList记录当前只出现了一次的值。
若当前遍历的值,在ArrayList中已经出现,则移除该值,继续遍历。
最后剩下的两个值,即为所求。

public class Solution {
        public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
                ArrayList<Integer>list=new ArrayList<Integer>();
                for(int i=0;i<array.length;i++)
                    {
                        if(!list.contains(array[i]))
                            list.add(array[i]);
                        else
                            list.remove(new Integer(array[i]));
                    }
                if(list.size()>1)
                    {
                        num1[0]=list.get(0);
                        num2[0]=list.get(1);
                    }
        }
}

方法四:暴力解法

对数组进行排序。
遍历一次数组,将array[i]分别与array[i-1]和array[i+1]作比较,若都不相同,则找到只出现一次的值。
注意处理第一个数字和最后一个数字的边界情况。

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {

        ArrayList<Integer> arr = new ArrayList<Integer>();
        Arrays.sort(array);
        for(int i=0, len=array.length; i<len; i++){
            if(i==len-1 && array[i]!=array[i-1]){
                arr.add(array[i]);
            }else if(i==0 && array[i]!=array[i+1]){
                arr.add(array[i]);
            }else{
                if(i!=0 && i!=len-1 && array[i]!=array[i-1] && array[i]!=array[i+1]){
                    arr.add(array[i]);
                }
            }
        }
        num1[0] = arr.get(0);
        num2[0] = arr.get(1);
    }
}
阅读更多

没有更多推荐了,返回首页