基础知识:
任何数和 0 做异或运算,结果仍然是原来的数,即 a⊕0=a
任何数和其自身做异或运算,结果是 0,即 a⊕a=0
异或运算满足交换律和结合律,即 a⊕b⊕a=b⊕a⊕a=b⊕(a⊕a)=b⊕0=b
使用位运算符可以实现 O(1) 的空间复杂度。
异或运算:x ^ 0 = x , x ^ 1 = ~x
与运算:x & 0 = 0 , x & 1 = x
singgle number1:整数类型的数组,数组中只有一个元素只出现一次,其余元素都出现2次。你需要找出只出现一次的元素
import java.util.*;
public class Solution {
/**
*
* @param A int整型一维数组
* @return int整型
*/
public int singleNumber (int[] A) {
int num=0;
for (int i=0;i< A.length;i++){
num ^=A[i];
}
return num;//最后结果即出现1次的那个数
}
}
single number2:整数类型的数组,数组中只有一个元素只出现一次,其余元素都出现3次。你需要找出只出现一次的元素
基础知识:
hashset:
它存储唯一元素并允许空值
它由HashMap支持
它不保持插入顺序
它不是线程安全的
import java.util.*;
public class Solution {
public int singleNumber(int[] A) {
int sumarray=0,sumset=0;
HashSet<Integer> set=new HashSet<>();
for(int a:A){
sumarray+=A[a];//所有的数加起来
set.add(A[a]);//set存放唯一元素(不重复)
}
for(int s:set){ //将每个元素相加(只出现一次)
sumset+=s;
}
return (3*sumset-sumarray)/2;
}
}
逐位考虑
我们单独看二进制某一位,先不看单独的那个数,其他所有数字都出现了 3 次,所以那一位是 1 的个数一定是 3 的倍数。
再考虑这个出现一次的数,如果这一位是 1 ,那么这一位 1 的次数模 3 为 1 ,否则的话模 3 就是 0 。
那么就很简单了,统计一下有多少个数这一位上是 1 ,然后模 3 取余数,结果就是这个单独的数这一位上的值了。
遍历 32 位整数的每一位,就可以得到这个单独的数是多少了。
推广到一般情况:
如果其他数都出现了 k次,一个数出现了一次。那么如果 k 是偶数,还是把所有的数异或起来就行了。如果 k 是奇数,那么统计每一位是 1 的个数,然后模 k 取余数就能得到那个单独的数了。
因为一共有三个状态,所以我们需要用两个变量来表示状态。用 once 表示是否在状态 1,用 twice 来表示是否在状态 2 。那么两个变量都为 0 就表示在状态 0 。然后可以得到如下的状态转移表:
once转移情况:
Once=o,twice=0,x=1
Once=1,twice=0,x=1
总结:once^x=1,twice=0 => once=(once^x) & (~twice)
twice转移情况:
Once=1,twice=0,x=1
Once=0,twice=1,x=0
总结:twice^x=1,twice^once=1 => twice=(twice^x) & (twice^once) 因为once 已经抢先一步转移过了,所以值已经变掉了,这两种情况下,once 都会转移到 0 ,所以判断条件直接用转移后的 once=0 就行了,随后转移就是 twice = (x^twice) & (~once) 。
import java.util.*;
public class Solution {
public int singleNumber(int[] A) {
int once=0,twice=0;
for(int num:A){
once=(once^num) &(~twice);
twice=(twice^num) &(~once);
}
return once;//出现一次的
}
}