[LeetCode] 136.只出现一次的数字
一、问题概述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1] 输出: 1
示例 2:
输入: [4,1,2,1,2] 输出: 4
链接:https://leetcode-cn.com/problems/single-number/
二、问题分析
如果不对此题不加时空限制的话,可以想出一些解法,例如:
-
二重循环暴力查找
每次选取数组中一个元素,再遍历整个数组看是否能找到相同的。若找不到则该元素出现一次。
这种方法最容易想到,但在最坏的情况下会遍历\(n^2\)次,时间复杂度为\(O(n^2)\)。
-
使用hash表记录每个元素出现次数
创建一个hash表,遍历一遍数组的同时将每个元素出现的次数记录在hash表中,之后遍历hash表从中找出出现次数为1的元素。
这种方法可以做到线性时间复杂度,但使用了额外的空间。
-
排序数组再遍历
先将数组排序,之后遍历数组,判断每个元素与相邻的元素是否相同。若该元素与左右相邻元素都不同,则此元素只出现一次。
这种方法也是我第一反应想到的,虽然没有使用额外的空间,但使用快排的话,时间复杂度为\(O(nlogn)\)。此外对边界条件的判断我也不是很熟练,因此代码写得也很臃肿。
参考了大佬们的题解后,发现我漏了一个很重要的条件,就是每个元素最多重复两次!因此就可以根据异或(XOR)运算的性质来巧妙地解这道题。
首先介绍一下异或运算有以下性质:
- 任何数和 0 做异或运算,结果仍然是原来的数,即 \(a\oplus0=a\)。
- 任何数和其自身做异或运算,结果是 0,即 \(a \oplus a=0\)。
- 异或运算满足交换律和结合律,即 \(a \oplus b \oplus a=b \oplus a \oplus a=b \oplus (a \oplus a)=b \oplus0=b\)。
基于此性质,我们就可以遍历一遍数组,将所有元素逐个进行异或运算。
并根据交换律和结合律,将重复的元素两两结合,经异或运算后得到0,最后就变成:
\[0\oplus0\oplus0\oplus ... \oplus0\oplus a=a
\]
因此,数组中的全部元素的异或运算结果即为数组中只出现一次的数字。
三、涉及算法
- 异或(XOR)运算
四、AC代码
class Solution {
public:
int singleNumber(vector<int>& nums) {
for (int i = 1; i < nums.size(); i++)
{
nums[0] ^= nums[i];
}
return nums[0];
}
};
五、复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)
作者:hanbin92381
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。