方法一:暴力

1.结题思路

暴力法统计元素出现次数并输出。

2.解法

暴力,遍历vector,并用map或set统计每个元素的出现次数再找出出现次数为1的数即可。

3.具体代码

vector<int> FindNumsAppearOnce(vector<int>& array) {
    vector<int>ans;
    map<int,int>m;
    for(vector<int>::iterator it=array.begin();it!=array.end();it++){//统计每个数字出现的次数 
        m[*it]++;
    }
    for(vector<int>::iterator it=array.begin();it!=array.end();it++){//找出出现次数为1的元素 
        if(m[*it]==1)ans.push_back(*it);
    }  
    return ans;      
}

4.时间复杂度和空间复杂度分析

时间复杂度:O(nlogn),n是数组元素个数,仅遍历了两次vector数组,使用map有序,时间复杂度为O(nlogn)。
空间复杂度:O(1),只存了两个出现一次的整形数。

方法二:位运算

1.结题思路

数组中所有元素按位异或,也即两个出现次数为一的数的异或,异或的好处在于消去了出现次数为二的元素,只保留下出现为1的数的特征。再根据特征求解即可。

2.解法

  • 所有元素按位异或,得到两个出现次数为一的数的异或。
  • 接着对两个出现次数为一的数的异或取lowbi运算;
    lowbit(n):非负整数n在二进制表示下“最低位的1及其后面所有的0”构成的数值。
    lowbit(n)=n&(~n+1)=n&(-n);
  • 由lowbit运算确定出两个出现次数为一的数不相同的一位,并根据该位对所有数组元素进行分组,必能将这两个数分到两组里去,其余元素出现次数为2相异或必为0,0再与这两个数分别异或就等于这两个数本身。

3.图解

图片说明

4.具体代码

vector<int> FindNumsAppearOnce(vector<int>& array) {
    vector<int>ans;
    int temp=0;//数组中所有元素按位异或
    for(int i=0;i<array.size();i++){
        temp^=array[i];
    } 
    temp=temp&(-temp);//temp在二进制表示下“最低位的1及其后面所有的0”构成的数
    int a=0,b=0;//0与任何数异或等于任何数本身
    for(int i=0;i<array.size();i++){//将vector中元素根据temp二进制中1所在位为1或为0分成两组 
        if((array[i]&temp)==0)a^=array[i];//对应位为0,相异或为0  
        else  b^=array[i];//对应位为1,相异或为temp
    }
    if(a>b)swap(a,b);//按大小加入ans
    ans.push_back(a);ans.push_back(b);
    return ans;      
}

5.时间复杂度和空间复杂度分析

时间复杂度:O(n),n是数组元素个数,代码中仅遍历了两次数组。
空间复杂度:O(1),只用到了ab两个常数级别的整形数据。