题目
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
分析
首先看到题目我先想到的是用两层循环的暴力解法(时间复杂度为n的平方)但很明显会超出时间限制,然后我就想将这个数组排序,就很容易得到那个出现一次的元素。另外的想法是通过hashset的不重复特性求解。最简单的想法是通过异或解决,因为任何一个数字异或它自己都等于0,也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中抵消掉了。
废话不多说,下面直接上代码,并附有详细解析。
解决方案
方法一:暴力法(c++)(注意:此方法在leetcode上运行超出时间限制,我在这里只是想展示一种思路)
class Solution {
public:
int singleNumber(vector<int>& nums) {
for(int i=0;i<nums.size();i++)//双重循环将nums中相等的值赋值为0
{
for(int j=0;j<i;j++)
{
if(nums[i]==nums[j]){
nums[i]=0;
nums[j]=0;
}
}
}
for(int i=0;i<nums.size();i++)//遍历nums找出唯一不为零的值并给nums[0],或者该元素为0则返回0,不影响结果
{
if(nums[i]!=0)
nums[0]=nums[i];
}
return nums[0];
}
};
方法二:排序(c++)
形式一:
class Solution {
public:
int singleNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());//排序
int temp;//作为返回值
if(nums.size()==1)//考虑nums只有一个的情况
return nums[0];
if(nums[0]!=nums[1])//考虑开头
return nums[0];
if(nums[nums.size()-1]!=nums[nums.size()-2])//考虑结尾
return nums[nums.size()-1];
for(int i=1;i<nums.size()-1;i++)//考虑中间
{
if(nums[i-1]!=nums[i]&&nums[i+1]!=nums[i])
temp= nums[i];
}
return temp;
}
};
形式二:先排序,再用双指针
class Solution {
public:
int singleNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());
for(int i = 0, j = 1; j < nums.size(); i += 2, j += 2){
if(nums[i] != nums[j]) return nums[i];
}
return nums[nums.size() - 1];
}
};
方法三:哈希集(Java)
class Solution {
public int singleNumber(int[] nums) {
int res=0;//作为返回值,必须初始化
HashSet<Integer>temp =new HashSet<Integer>();
for(int x:nums)//遍历nums,先判断temp中是否包含x,若包含则将temp中的x值删除,否则继续向temp中添加新值
{
if(temp.contains(x))
temp.remove(x);
else
temp.add(x);
}
for(int y:temp)
res=y;
return res;
}
}
方法四:异或(c++)
任何一个数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中抵消掉了。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res=nums[0];
for(int i=1;i<nums.size();i++)
res=res^nums[i];
return res;
}
};