给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
思路:
方法一:设置两个指针,一前一后。当两者不等时,后指针后移。当两者相等,则在两个指针位上标记flag,下次不用扫描这两个成员,同时前指针后移,后指针=前指针+1。当后指针遍历完数组仍找不到和前指针相同元素时,前指针元素即为目标。
空间复杂度O(n),因为要建立一个带有标记flag和原有成员的数据结构数组;时间复杂度O(n^2)。
显然,这是最蠢的办法。
方法二:遍历一遍数组,记录最大值max。建立一个有max个元素的bool类型的数组,初值为0。然后再遍历原数组,在原数组的成员的数值所对应编号的bool数组成员上取反。原数组遍历完毕,再遍历bool数组,值为1的元素下标值+1即为目标数值。
局限性很大,若数值大则空间复杂度极高。时间复杂度O(n)。
方法三:拿到题目后隐约感觉要用位运算求解,但是并没有清晰的思路。查阅资料后才知要用异或法求解。
异或,英文为exclusive OR,缩写成xor,是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。其运算法则为:
a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。
简单来说,就是相同得0,不同得1。
运算法则:
- a ⊕ a = 0
- a ⊕ b = b ⊕ a
- a ⊕b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c;
- d = a ⊕ b ⊕ c 可以推出 a = d ⊕ b ⊕ c.
- a ⊕ b ⊕ a = b.
- a ⊕ 0 = a
例:若x是二进制数0101,y是二进制数1011,则x⊕y=1110。
只有在两个比较的位不同时其结果是1,否则结果为0。即“两个输入相同时为0,不同则为1”!
代码:
int singleNumber(int* nums, int numsSize) {
int a=0;
for(int i=0;i<numsSize;i++)
{
a=a^nums[i];
}
return a;
}