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?
感谢cnblog博主feiling,这篇博客一方面参考了feiling的博客,也加入了自己的一些看法。
[解题思路]
要求线性时间复杂度,同时空间复杂度为O(1),即只允许开常数个空间。
最直接的思路是对每一个元素尝试查找是否有重,如果没有重,就返回。
class Solution { public: int singleNumber(int A[], int n) { int i = 0, j = 0; for(i = 0;i < n;i++) { for(j = 0;j < n;j++) { if(i == j) continue; else if(A[i] == A[j]) break; else continue; } if(j == n) return A[i]; }
return 0;
}
};
不幸的是虽然是o(n2)的时间复杂度,但是还是超时了。于是乎就得想一个o(n)的算法。
o(n)的算法只能是线性扫描一遍,可能的相法是位运算。对于异或来说:
1. 异或运算是可交换,即 a ^ b = b ^ a
2. 0 ^ a = a
那么如果对所有元素做异或运算,其结果为那个出现一次的元素,理解是a1 ^ a2 ^ ....,可以将所有相同元素交换至相邻位置,首先运算相同元素,则会产生(n - 1)/2个0异或积,剩余一个单一元素,他们的异或积为这个单一元素自己,得解。
1 public class Solution { 2 public int singleNumber(int[] A) { 3 // Note: The Solution object is instantiated only once and is reused by each test case. 4 if(A == null || A.length == 0){ 5 return 0; 6 } 7 int result = A[0]; 8 9 for(int i = 1; i < A.length; i++){ 10 result = result ^ A[i]; 11 } 12 return result; 13 } 14 }
本题扩展
1.一个数组中有两个元素只出现一次,其他所有元素都出现两次,求这两个只出现一次的元素
public static List findOnlyNum(int[] array){
List<Integer> list = new ArrayList<Integer>();
for(int i = 0 ;i<array.length;i++){
int j = 0;
for(;j<array.length;j++){
if(i==j){
continue;
}
if(array[i]==array[j]){
break;
}
}
if(j==array.length){
list.add(array[i]);
}
}
return list;
}
[解题思路]
将数组所有元素都进行异或得到一个不为0的结果,根据这个结果中的不为0的某一位将数组分成两组
将两组中的元素进行异或,如两个数组的异或值都不为0,则得到最后结果
public static List findOnlyNum(int[] array){
List<Integer> list = new ArrayList<Integer>();
int result = 0;
int result1 = 0;
int result2 = 0;
//我们需要找到一个条件,给这两个出现过一次的数找出可以区分的条件。
//相同的数异或等到的结果0,那么整个序列异或的结果就是这两个出现过一次的数的异或。
for(int i = 0 ;i<array.length;i++){
result^=array[i];
}
//找出他们的不同之处,前面我们讲过,异或按位操作是相同的为0 ,不同的为1,那么这两个数异或的结果转换成2进制时,低位出现第一个1时就可以区分他们。
String binaryResult = Integer.toBinaryString(result);
int index = binaryResult.length() - (binaryResult.lastIndexOf("1")+1);
//在index位为1的分为一组,为0的分为一组,将序列划分为两个序列
for(int i =0;i<array.length;i++){
if(((array[i]>>index)&1)==1){
result1^= array[i];
}else{
result2^=array[i];
}
}
list.add(result1);
list.add(result2);
return list;
}
假设二个不同数值是a,b;那么 s = a^b; 然后求的s中第一次出现1的位
置k,根据异或运算特性,第一次出现1的地方就是这二个数位有区别的地方,比如1001 0101 与 1100 0001 得到异或
结果是0101 0100 .那么第一次出现1的地方是第三位,a的第三位是1而b是0 。 接着遍历数值,找出第三位是1的数值就异或,得到的结果就是其中一个数与其他出现二次的数求异或,因为出现二次的
数异或得到为0所
有最后结果就是所求其中一个数。
最后s与该数求异或就得到另一个数。
2.一个数组中有一个元素只出现1次,其他所有元素都出现k次,求这个只出现1次的元素
[解题思路]
当k为偶数时,同lss
当k为奇数时,将数组中每个元素的每一位相加mod k,得到结果即位出现1次的元素,时间复杂度O(nlen),空间复杂度为O(1)