document.write('
方法一:字典法
对于本题而言,定义一个字典,把数组元素的值作为key,遍历整个数组,如果key值不存在,则将
value设为1,如果key值已经存在,则翻转该值(如果为0,则翻转为1;如果为1,则翻转为0),在完成
数组遍历后,字典中value为1的就是出现奇数次的数。
例如:给定数组=[3,5,6,6,5,7,2,2];
首先遍历3,字典中的元素为:{3:1};
遍历5,字典中的元素为:{3:1,5:1};
遍历6,字典中的元素为:{3:1,5:1,6:1};
遍历6,字典中的元素为:{3:1,5:1,6:0};
遍历5,字典中的元素为:{3:1,5:0,6:0};
遍历7,字典中的元素为:{3:1,5:0,6:0,7:1};
遍历2,字典中的元素为:{3:1,5:0,6:0,7:1,2:1};
遍历2,字典中的元素为:{3:1,5:0,6:0,7:1,2:0};
显然,出现1次的数组元素为3和7。
实现代码如下:
def get2Num(arr):
if arr==None or len(arr)<1:
print "参数不合理"
return
dic=dict()
i=0
while i<len(arr):
#dic中没有这个数字, 说明第一次出现, value赋值为1
if arr[i] notin dic:
dic[arr[i]]=1
#当前遍历的值在dic中存在,说明前面出现过,value赋值为0
else:
dic[arr[i]]=0
i+=1
for k,v in dic.items():
if v==1:
print int(k)
if __name__=="__main__":
arr=[3,5,6,6,5,7,2,2]
get2Num(arr)
程序输出为:
3
7
性能分析:
这种方法对数组进行了一次遍历,时间复杂度为O(n)。但是申请了额外的存储过程来记录数据出现的
情况,因此,空间复杂度为O(n)。
方法二:异或法
根据异或运算的性质不难发现,任何一个数字异或它自己其结果都等于0。所以,对于本题中的数组
元素而言,如果从头到尾依次异或每一个元素,那么异或运算的结果自然也就是那个只出现奇数次的数
字,因为出现偶数次的数字会通过异或运算全部消掉。
但是通过异或运算,也仅仅只是消除掉了所有出现偶数次数的数字,最后异或运算的结果肯定是那两
个出现了奇数次的数异或运算的结果。假设这两个出现奇数次的数分别为a与b,根据异或运算的性
质,将二者异或运算的结果记为c,由于a与b不相等,所以,c的值自然也不会为0,此时只需知道c对
应的二进制数中某一个位为1的位数N,例如,十进制数44可以由二进制0010 1100表示,此时可取
N=2或者3,或者5,然后将c与数组中第N位为1的数进行异或,异或结果就是a,b中一个,然后用c异
或其中一个数,就可以求出另外一个数了。
通过上述方法为什么就能得到问题的解呢?其实很简单,因为c中第N位为1表示a或b中有一个数的第
N位也为1,假设该数为a,那么,当将c与数组中第N位为1的数进行异或时,也就是将x与a外加上其他
第N位为1的出现过偶数次的数进行异或,化简即为x与a异或,结果即为b。
示例代码如下:
def get2Num(arr):
if arr==None or len(arr)<1:
print "参数不合理"
return
result=0
position=0
#计算数组中所有数字异或的结果
i=0
while i<len(arr):
result=result^arr[i]
i+=1
tmpResult=result #临时保存异或结果
#找出异或结果中其中一个位值为1的位数(如1100,位值为1位数为2和3)
i=result
while i&1=0:
position+=1
i=i>>1
i=1
while i<len(arr):
#异或的结果与所有第position位为1的数异或,结果一定是出现一次的两个数中其中一个
if ((arr[i]>>position)&1)==1:
result=result^arr[i]
i+=1
print result,
#得到另外一个出现一次的数
print result^tmpResult
if __name__="__main__":
arr[3,5,6,6,5,7,2,2]
get2Num(arr)
程序的运行结果为:
3 7
算法性能分析:
这种方法首先对数组进行了一次遍历,其时间复杂度为O(N),接着找result对应二进制数中位值为1的位数,时间复杂度为O(1),接着又遍历了一次数组,时间复杂度为O(N),因此,这种方法整体的时间复杂度为O(N)。
');