只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

输入: [4,1,2,1,2]
输出: 4

 

方法 1:集合操作
算法

遍历nums 中的每一个元素
如果某个nums 中的数字是新出现的,则将它添加到集合中
如果某个数字已经在集合中,删除它

#include<iostream>
#include<set>
#include<iterator>
using namespace std;
set<int>st;
int main(){
    int num;
    set<int>::iterator it= st.begin();
    while (cin>>num){
        it= st.find(num);
        if(it==st.end())
            st.insert(num);
        else
            st.erase(it);
    }
  for (it = st.begin(); it != st.end(); it++)
        cout << *it << " ";
}

 关于Set 的STL 知识点:https://www.cnblogs.com/BlairGrowing/p/12709247.html

复杂度分析

时间复杂度:O(n2)     我们遍历nums 花费 O(n)的时间。我们还要在列表中遍历判断是否存在这个数字,花费 O(n)的时间,所以总循环时间为 O(n2)
空间复杂度:O(n) 。我们需要一个大小为 n 的列表保存所有的 nums 中元素。

 

方法 2:哈希表
算法

我们用哈希表避免每次查找元素是否存在需要的 O(n)时间。

遍历 nums 中的所有元素并建立键/值对。
返回只出现一次的元素。

 

int singleNumber(vector<int>& nums) {
        map<int,int> m;
        for(int i=0; i<nums.size();i++){
            if(m.find(nums[i]) != m.end()){
                m.erase(m.find(nums[i]));
            }else{
                m[nums[i]] = 1;
            }
        }
        return m.begin()->first;
}
int main(){
    int n;
    int arr[] = {4,1,2,1,2};
    vector<int> nums(arr, arr + sizeof(arr) / sizeof(int));
    cout<<singleNumber(nums);
}

 

复杂度分析

时间复杂度:O(n⋅1)=O(n) 。for 循环的时间复杂度是 O(n) 的。Python 中哈希表的 pop 操作时间复杂度为O(1)。
空间复杂度: O(n) 。hash_table 需要的空间与 nums 中元素个数相等。

 

方法 3:数学

概念

2 * (a + b + c) - (a + a + b + b + c) =c

    set<int>st;
    int sum=0;
    int arr[] = {4,1,2,1,2};
    set<int>::iterator it;
    st.insert(arr,arr+5);
    for (it = st.begin(); it != st.end(); it++)
        sum+=*it;
    sum*=2;
    for (int i = 0; i < 5; i++)
    {
        sum-=arr[i];
    }
    cout<<sum<<endl;

 

方法 4:按位异或

按位异或(xor)操作符

  按位操作符的字面意思很好理解,即对值的二进制格式进行处理的操作符。而异或的作用为:假设有值甲、乙,当甲乙值相等时,异或操作后结果为不等(False,0),反之,为相等(True,1)。所以按位异或操作符的释义便显而易见了:对某值的每个位上的值(0或1)进行异或操作。

举一例例子:

甲:0 0 0 0 1 1 0 0 (值为12)

乙:0 0 0 0 0 1 1 1 (值为7)

甲和乙进行按位异或操作得到新值丙

丙:0 0 0 0 1 0 1 1 (值为11)

可见,当两值的某一位的值不同时,按位异或操作后所得新值某一位的值将为 1 (如从右到左第一位),反之为 0 (如从右到左第三位)。

既然有异或操作符,那么很可能会有同按位的操作符,但我就不再一一介绍了,毕竟我介绍按位异或操作符只是为显得我写的题解内容翔(空)实(洞)。

逻辑思路

有了按位异或操作符,我们就可以依照其独特的性质,敲除一段极其华丽的代码。

但题解是题解,不是我擅长的搬砖,我得先把逻辑思路捋清。

有一个数组,含有值:12,7,12。并含有一个变量甲,其值为0。(先不要纠结为何要这么定义,这是为了了解按位异或操作符的神奇性质)

对数组进行遍历,且遍历所得的每个值都和变量甲进行一次按位异或操作。

——————第一次:

12:0 0 0 0 1 1 0 0

甲:0 0 0 0 0 0 0 0

异或

甲:0 0 0 0 1 1 0 0(值为12)

——————第二次

7:0 0 0 0 0 1 1 1

甲:0 0 0 0 1 1 0 0

异或

甲:0 0 0 0 1 0 1 1(值为11)

——————第三次

12:0 0 0 0 1 1 0 0

甲:0 0 0 0 1 0 1 1

异或

甲:0 0 0 0 0 1 1 1(值为7)

——————遍历结束
返回值:甲。甲值为 7 ,数组中唯一元素为 7。

 

代码:

int main(){
    int n;
    set<int>st;
    int ans=0;
    int arr[] = {4,1,2,1,2};
    for(int i=0;i<5;i++){
        ans^=arr[i];
    }
    cout<<ans<<endl;
}

 

posted @ 2020-04-25 16:50  关注我更新论文解读  阅读(82)  评论(0编辑  收藏  举报