题目
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
分析
1 本来想的是用Java里的排序,排完顺序之后,再分第一个位置 和中间位置处理, 最后返回最后一个位置
这样的做法显然不是很好,不说用到了Java里的arrays.sort 在找出唯一的那个不是出现两次的元素用到的方法也显得很蠢。
2 后来才知道这题考的是 位运算 处理,我们知道,两个相同的数字的二进制取与操作的结果是为0,所以数组中的所有相同的元素就可以相互抵消,最后剩下的就是那个出现一次的数字,最后返回就好了
Java 里异或操作符 不同的为1 相同的为0
~ 取反运算符
& 与操作符
代码
class Solution {
public int singleNumber(int[] nums) {
int result = nums[0];
for(int i = 1; i < nums.length; i++){
result^=nums[i];
}
return result;
// Arrays.sort(nums);
// for (int i = 0; i < nums.length - 1; i++) {
// if(i == 0 && nums[i + 1] != nums[i]){
// return nums[0];
// }else if(i != 0 && nums[i] != nums[i - 1] && nums[i] != nums[i + 1]){
// return nums[i];
// }
// }
// return nums[nums.length - 1];
}
}
扩展1
数组中其他数字都出现三次,只有一个出现一次,求出这个数
两位 表示四种状态 00 10 01 11 我们只需要三种状态 00 - 10 - 01 - 00 分别是初始状态,赋值到ones, 赋值到twos, 清空所有
ones twos 两个bit位存储
代码
class Solution {
public int singleNumber(int[] nums) {
int ones = 0, twos = 0;
for(int i = 0; i< nums.length; i++){
ones = (ones ^ nums[i]) & ~twos;
twos = (twos ^ nums[i]) & ~ones;
}
return ones;
}
}
time: o(n)
space:o(1)
不用位运算,用hashmap
key 存放元素的值,value存放次数
最后找出value为1的值
拓展2
一个数组其他两次,有两个只出现了一次,求这两个数
分析
1 异或所有的数
2 diff 与上 diff的补码形式 反码加1
例
3 011
5 101
3&5 = 110 = diff = 6
-diff = 1111010
与上结果为 10
结论 a b不相同,那么ab的二进制有且只有一位是不相同的
6 & -6 10
那么得知 不同的一位是倒数第二位
就将原数组分为两个 一个是第二位为1 一个为0 分别对这两个数组取异或运算,就能得到最终结果
代码
public int[] singleNumber(int[] nums) {
int diff = 0;
for (int i = 0; i < nums.length; i++) {
diff ^= nums[i];
}
diff &= -diff;
int res[] = new int[2];
for (int i = 0; i < nums.length; i++) {
if((nums[i] & diff) == 0){
res[0] ^= nums[i];
}else{
res[1] ^= nums[i];
}
}
return res;
}