异或运算有以下三个性质。
1.任何数和 0做异或运算,结果仍然是原来的数,即a^0=a
;
2.任何数和其自身做异或运算,结果是 0,即a^a=0
;
3.异或运算满足交换律和结合律,即a^b^a=b^a^a=b^(a^a)=b^0=b
;
给定两个整数a和b
a = a ^ a ^ b = b;
b = b ^ b ^ a = a;
a = a ^ b;
b = a ^ b;
a = a ^ b;
0xaaaaaaaa 和 0xAAAAAAAA 一样,表示所有偶数位为1
0x55555555表示所有奇数位为1
for (int i = 0; i < len - 1; i++) {
for (int j = i + 1; j < len; j++) {
}
}
for(int i = 0;i < len;i ++){
for(int j = 0;j < i;j ++){
}
}
Brian Kernighan算法可以用于清除二进制数中最右侧的1。
x&(x-1)
1.x=111,x-1 = 110,x&(x-1)=110,清除了最右侧的1
2.x=110,x-1 = 101,x&(x-1)=100,清除了最右侧的1
//1.异或运算
public int singleNumber(int[] nums) {
int len = nums.length;
int res = nums[0];
for(int i = 1;i < len;i ++){
res ^= nums[i];
}
return res;
}
//1.利用javaAPI
public static int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
//2.移位计算
public static int hammingDistance2(int x, int y) {
int z = x ^ y;
int count = 0;
while (z > 0){
//看z的最后一位是否为1
count += z & 1;
//z右移
z >>= 1;
}
return count;
}
//3.Brian Kernighan 算法(只统计1的个数,略过0)
public static int hammingDistance3(int x, int y) {
int z = x ^ y;
int count = 0;
while (z != 0){
z &= z - 1;
count ++;
}
return count;
}
//1.数学方法
public static int missingNumber(int[] nums) {
int sum = 0;
for (int i : nums){
sum += i;
}
int len = nums.length;
return len * (1 + len) / 2 - sum;
}
//2.位运算
public static int missingNumber(int[] nums) {
int len = nums.length;
int sum = 0;
for (int i = 0; i < len; i++) {
sum ^= (i ^ nums[i]);
}
return sum ^ len;
}
//1.哈希集合(最先想到的方法)
public static int[] singleNumber(int[] nums) {
//或者是用map也可以
Set<Integer> set = new HashSet<>();
for (int i : nums){
if (set.contains(i)){
set.remove(i);
}else{
set.add(i);
}
}
int[] res = new int[2];
int j = 0;
for (int i : set){
res[j ++] = i;
}
return res;
}
//2.位运算
/*
* 就是通过先求二进制异或和,选择一个为1的位,那么两个数在这个位上一
* 定是一个为1一个为0,通过与运算就可以把它们分组,就变成了只出现一次
* 的数字Ⅰ问题,再异或一次就好
* */
public static int[] singleNumber2(int[] nums) {
int nAdd = 0;
for (int num : nums){
nAdd ^= num;
}
int i = 1;
while ((i & nAdd) == 0){
i <<= 1;
}
int nAdd1 = 0,nAdd2 = 0;
for (int num : nums){
if ((i & num) != 0){
nAdd1 ^= num;
}else{
nAdd2 ^= num;
}
}
return new int[]{nAdd1,nAdd2};
}
//1.位运算
public int reverseBits(int n) {
int res = 0;
for(int i = 0;i < 32;i ++){
if(n == 0) break;
//找到为1的位并反转
int temp = ((n & 1) << (31 - i));
//将所有反转之后的1组合
res |= temp;
//n无符号右移
n >>>= 1;
}
return res;
}
//2.调用javaAPI
public int reverseBits2(int n) {
return Integer.reverse(n);
}
//1.自己想的,统计1出现的次数
public boolean isPowerOfTwo(int n) {
int countOne = 0;
while(n > 0){
countOne += (n & 1);
n >>= 1;
}
return countOne == 1 ? true : false;
}
//2.技巧
public static boolean isPowerOfTwo2(int n) {
//如果n是2的幂次方,则n和n-1的每一个对应位必定是一个为0一个为1
return n > 0 && (n & (n - 1)) == 0;
}
//3.技巧
public static boolean isPowerOfTwo3(int n) {
//如果n是2的幂次方,则n和-n按位做与,最终结果定是n
return n > 0 && (n & -n) == n;
}
//4.技巧
public static boolean isPowerOfTwo4(int n) {
return n > 0 && Integer.bitCount(n) == 1;
}
//1.使用循环
public static boolean isPowerOfFour(int n) {
while ( n > 0 && Integer.bitCount(n) == 1){
if ((n & 1) == 1){
return true;
}
n >>= 2;
}
return false;
}
//2.技巧
public static boolean isPowerOfFour2(int n) {
//0xaaaaaaaa 和 0xAAAAAAAA 一样,表示所有偶数位为1
return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;
// 0x55555555表示所有奇数位为1
// return n > 0 && (n & (n - 1)) == 0 && (n & 0x55555555) != 0;
}
//3.技巧
public static boolean isPowerOfFour3(int n) {
//是2的幂次方并且模上3为1,则为4的幂次方
//假设4的幂次方为4^x,则4^x % 3 = 4^(x-1) * (4%3) = 4^(x-1),所以最终为1
return n > 0 && (n & (n - 1)) == 0 && n % 3 == 1;
}
//1.自己想的
public static boolean hasAlternatingBits(int n) {
//看相邻位是否相同
while (n > 0){
if ((n & 1) == (n >> 1 & 1)){
return false;
}
n >>= 1;
}
return true;
}
/*2.
* 分析:
* 如果n是交替的01,对于它右移一位后得到的m,
* 存在n跟m在二进制下必然是0和1对应的(对位)。异或运算必定都是1;
*
* 举个栗子:5=101 5>>1=10,5^(5>>1)=111
* 101
* 10 =111
*假设异或结果共有N位为1,则将此结果加1之后,第N+1位为1,从1到N位都为0,
*此时再将两者做与运算,则最终结果必为0
*/
public static boolean hasAlternatingBits2(int n) {
int temp=n^(n>>1);
return (temp&(temp+1))==0;
}
//3.和1的思路是一样的
public static boolean hasAlternatingBits3(int n) {
while (n > 0){
if ((n % 2 == n / 2 % 2)){
return false;
}
n /= 2;
}
return true;
}
//1.自己想的
public int findComplement(int num) {
int res = 0;
//用来记录当前到了第几位
int count = 0;
while(num > 0){
//如果当前位为1,则取反为0,不计数
if((num & 1) == 1){
count ++;
}else{
//如果当前位为0,则取反为1,计数
res += (1 << count);
count ++;
}
num >>= 1;
}
return res;
}
//2.求掩码
/*
思路:
5 -> 101 , 其补码为 010 ,将 101 与 111 做 异或即可达到他对应的补码
问题的关键为:求掩码
*/
/*
1 -> 1
11 -> 3
111 -> 7
1111 -> 15
...
* */
public static int findComplement2(int num) {
int pre = 1,i = 1;
while (pre < num){
pre += Math.pow(2,i);
i ++;
}
return pre ^ num;
}
//3.思路和1一样
public static int findComplement3(int num) {
int temp = num;
int pre = 0;
while(temp > 0){
temp >>= 1;
pre = (pre << 1) + 1;
}
return num ^ pre;
}
//1.位运算
//可以将b作为进位来看待
public int getSum(int a, int b) {
//如果存在进位,就要加入到总和中去
while(b != 0){
//计算对应位能形成进位的位置
int temp = (a & b) << 1;
//计算对应位不能形成进位的位置
a = a ^ b;
b = temp;
}
//如果不存在进位了,返回结果
return a;
}
//1.位运算
public static int maxProduct(String[] words) {
int len = words.length;
int[] arr = new int[len];
for (int i = 0;i < len;i ++){
for (char c : words[i].toCharArray()){
/*
arr[i]记录的是第i个字符串中26个字母是否出现
abc -> 000111
def -> 111000
如果相与为0,则两个字符串中没有相同的字母
* */
arr[i] |= 1 << (c - 'a');
}
}
int max = 0;
for (int i = 0; i < len - 1; i++) {
for (int j = i + 1; j < len; j++) {
if ((arr[i] & arr[j]) == 0){
max = Math.max(max,words[i].length() * words[j].length());
}
}
}
// for(int i = 0;i < len;i ++){
// for(int j = 0;j < i;j ++){
// if ((arr[i] & arr[j]) == 0){
// max = Math.max(max,words[i].length() * words[j].length());
// }
// }
// }
return max;
}
//1.分别计算每个数的位为1的个数,再加和,时间复杂度为O(nlogn)
public int[] countBits(int n) {
int[] ans = new int[n + 1];
for (int i = 0; i <= n; i++) {
ans[i] = countOne(i);
}
return ans;
}
public int countOne(int n) {
int res = 0;
while (n > 0) {
if ((n & 1) == 1) {
res++;
}
n >>= 1;
}
return res;
}
//2.分奇数偶数,时间复杂度为O(n)
/*
* 偶数的二进制1的个数超级简单,因为偶数是相当于被某个更小的数乘2,
* 乘2怎么来的?在二进制运算中,就是左移一位,也就是在低位多加1
* 个0,那样就说明dp[i] = dp[i / 2]
* 奇数稍微难想到一点,奇数由不大于该数的偶数+1得到,偶数+1在二
* 进制位上会发生什么?会在低位多加1个1,那样就说明dp[i] = dp[i-1] + 1,
* */
public int[] countBits2(int n) {
int[] dp = new int[n + 1];
for (int i = 0; i <= n; i++) {
if (i % 2 == 0) {
dp[i] = dp[i / 2];
} else {
dp[i] = dp[i - 1] + 1;
}
}
return dp;
}
//3.位运算
/*
* i&(i-1):此值必定比i(i>0)小
* 1.如果i的最低位为1,i&(i-1)相当于去掉i最低位上的1,如i=11,i&(i-1) = 10
* 2.如果i的最低位不为1,i&(i-1)相当于去掉从最低位向最高位遍历过程中第一个不为0的位上的1
* 如i=110,i&(i-1) = 100;i=100,i&(i-1)=000;
* 总的来说:就是清除掉最右侧的1
*
* 因为i & (i - 1)的1的个数已经在前面算过了,所以i的1的个数就是 i & (i - 1)的
* 1的个数加上1
* */
public int[] countBits3(int n) {
int[] res = new int[n + 1];
for (int i = 1; i <= n; i++) {
res[i] = res[i & (i - 1)] + 1;
}
return res;
}
//4.位运算
/*
* i >> 1会把最低位去掉,因此i >> 1 也是比i小的,同样也是在前面的数组里算过。
* 1.当 i 的最低位是0,则 i 中1的个数和i >> 1中1的个数相同;
* 2.当i的最低位是1,i 中1的个数是 i >> 1中1的个数再加1
* */
public int[] countBits4(int n) {
int[] dp = new int[n + 1];
for (int i = 0; i <= n; i++) {
dp[i] = dp[i >> 1] + (i & 1);
}
return dp;
}