leetcode 136.Single Number
题目
Given an array of integers, every element appears twice except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
翻译过来就是,一堆数字里面,除了一个元素只出现一次外,其余都出现了两次。找出那个只出现一次的。
例如,【2,2,1】 程序输出结果应为1.
解题思路
光看题目的话,这道题很简单。稍微想想大概就有4种算法:
1. 双重循环。
这是最容易想到的,一个一个数比较就行。此处不给出具体代码,但需要注意的是这种算法的时间复杂度为O(n^2),并不能AC,会超过时间限制。
用一个数组标记某个元素是否出现过。
我刚看到题目时就是这个思路,因为之前做过一道类似的题目——找寻只出现一次的ASCII码。但在做这道题时我发现好像行不通,ASCII码可以通过一个bool【256】的数组来标记,在本题中因为输入的数字大小不确定,所以没法建立一个合适大小的数组,用new的话可能又比较浪费空间。所以这种想法GG。稍稍改变一下思路,排序+比较相邻元素 好像行得通的样子。
具体思路就是:先对输入数组排序,然后比较相邻元素,步长为2.这里要介绍一下STL里的sort排序函数,用的是快速排序,时间复杂度为O(nlogn)。不用自己写排序算法,程序就非常简单了。
详见AC代码思路3使用STL中的set
unordered_set是C++11中刚刚加入的,它基于哈希表,一个存储唯一(Unique,即无重复)元素的关联容器,容器中的元素无特别的次序关系。该容器允许基于值地快速元素检索。这里不做具体介绍,这里用它是因为其检索十分快速,我们可以用find函数很快寻找是否有相同元素,然后再做删除或插入操作即可。
详见AC代码思路4用异或,从位操作的角度考虑。
可以看到leetcode里面要求的时间复杂度前几种算法已经满足了,但上述几种算法都需要额外的数据储存空间。而leetcode要求的是不使用额外的存储空间,这个我想了半天也没有想法。后来参考的leetcode上的大神代码才恍然大悟。嗯,和大神差距还是非常非常大的。
算法很简单,就是用异或运算符。说几个很容易证明的结论,A^0=A,A^A=0.即A与0的异或是其自身,而两个相同数字的异或结果为0.知道了这个,算法也就很容易了,将数组逐个异或,结果就是只出现过一次的值。
详见AC代码思路5
AC代码如下
思路3:
//时间32ms
class Solution {
public:
int singleNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int index=0;
while(index<nums.size()-1){
if(nums[index]==nums[index+1])
index+=2;
else
return nums[index];
}
return nums[index];
}
};
思路4:
//19ms
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_set<int> ans;
for(int i = 0;i < nums.size();++i){
if(ans.find(nums[i]) == ans.end())
ans.insert(nums[i]);
else ans.erase(nums[i]);
}
auto it = ans.begin();
return *it;
}
};
思路5:
//13ms
class Solution {
public:
int singleNumber(vector<int>& nums) {
int result=0;
for(int i=0;i<nums.size();i++){
result^=nums[i];
}
return result;
}
};
总结
这道题在leetcode上是easy难度,但要完全实现时间复杂度线性,无需额外空间,如果没有考虑到位操作,很难实现。
我们现在来看看每个思路运行的时间,可以看到排序+比较 最慢,可能是因为排序耗费了较多时间;用unordered_set的运行时间提高了不少,可以看到选择合适的数据结构很重要;最后的异或算法自然是运行时间最短,另外不需要额外空间。如果面试官限制了不允许使用STL及内置函数,那这道题异或算法无疑是面试官最想听到的答案。
最后总结一下,遇到这种可能有大量数据、空间复杂度要求特别严苛,普通数据结构或算法没法达到的,就可以考虑考虑位操作了。对bit的操作,一般来说,时间复杂度和空间复杂度都十分优秀。
最后一些话
今天收到了摩根IT暑期实习的拒信,有点伤心,自己确实能力不够,知识积累也不多,好像也没啥可抱怨的。Talk is cheap,show your code. 慢慢积累,慢慢进步。与诸君一起进步!