题目:
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?
分析:
给定一整数数组,数组中只有一个元素出现了1次,剩下其他的元素均出现了两次,要求找出这个“落单”的元素。题目要求给出线性的时间复杂度以及不使用额外空间。
不考虑复杂度的话,其实我们需要做的是,寻找出现2次和出现1次的元素之间的区别,并根据这个区别来选出要求的元素。很自然地,我们会想到遍历该数组,记录元素出现的次数,输出那个独行侠,如下(java 实现):
public class Solution {
public int singleNumber(int[] A) {
// 检查边界条件
if(A==null) {
return 0;
}
int length=A.length;
if(length==0) {
return 0;
}
if(length==1){
return A[0];
}
//先排序,然后对比相邻的两个元素
Arrays.sort(A);
for(int i=0;i<length;i+=2) {
if(i==length-1) {
return A[i];
}
if(A[i]==A[i+1]) {
continue;
}
return A[i];
}
return 0;
}
}
如果考虑到题目对复杂度的要求,这种简单的遍历就不太合适了。我们注意到,这个遍历实际上跳过了相同的元素对,从而最终留下了那个与众不同的元素。因此我们需要一种对“不同”敏感的操作,异或(XOR)。针对本题的需求,按位异或操作^可以简便地解决这个问题,如下(java和python 实现):
java:
public class Solution {
public int singleNumber(int[] A) {
if(A==null || A.length==0) {
return 0;
}
int result = A[0];
for(int i=1; i<A.length; i++) {
result ^=A[i];
}
return result;
}
}
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
res =0;
for num in nums:
res = res^num
return res
探讨:
按位异或^的特性是相同则为0,不同则为1。于是对于整数A,有A^A=0,A^0=A。同时由于异或运算满足交换律和结合律,所以前述异或解法实际上将众相同的元素转化为了0,最后将落单元素X与0异或得到X。
按位异或运算的另一常用用法是在不使用额外变量的情况下交换两个整型变量的值,如下(java 实现):
public static void swap_opt(int a, int b) {
a = a^b;
b = a^b;
a = a^b;
}
由结合律可知,首先有b=(a^b)^b=a^(b^b)=a^0=a,然后有a=(a^b)^b=(a^b)^a=(a^a)^b=0^b=b。
同一原理也可以应用于简单的加密解密算法中。对任意待加密数据D,可以用密钥K对其进行逐位的异或,得到加密的结果X;解密时,只需要用K再对X进行逐位的异或,即可获得D。
此外,对于python解法,也可以采用reduce和lambda函数来实现快捷遍历:
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
return reduce(lambda x, y: x ^ y, nums)
当然,如果对空间复杂度不做要求的话,使用hashset也可以较为漂亮地解答本题。