预备知识
给一个数n,确定它的二进制表示中的第x位是0还是1
(n>>(x-1)) & 1 == 1
true是1,false是0
将一个数n的二进制表示的第x位修改成1
n |= (1<<(x-1))
将一个数n的二进制表示的第x位修改成0
n &= (~(1<<(x-1)))
提取一个数n二进制表示中最右侧的1
n & -n
-n 相比 n 是将最右侧的1左边的区域全部取反
干掉一个数n二进制表示中最右侧的1
n = n & (n-1)
n-1是将最右侧的1右边的区域(包含1)全部取反
异或(^)运算的运算律
1.两个相同的数异或的结果一定为0。(a^a== 0)
2.任何数与0异或都等于它自己。(a^0== a)
3.异或满足交换律;
判断字符是否唯⼀
算法思路(位图的思想):
利用「位图」的思想,每⼀个「比特位」代表⼀个字符,⼀个 int 类型的变量有 32 位足够表示所有的小写字⺟。比特位里面如果是 0 ,表示这个字符没有出现过。比特位里面的值是 1 表示该字符出现过。
那么我们就可以用⼀个「整数」来充当「哈希表」。
class Solution {
public boolean isUnique(String astr) {
//利用鸽巢原理来做优化
if(astr.length() > 26) return false;
int bitMap = 0;
for(int i = 0; i < astr.length(); i++) {
int x = astr.charAt(i) - 'a';
//先判断字符是否在位图中
if(((bitMap >> x) & 1) == 1) return false;
//把当前字符加入到位图中
bitMap |= 1 << x;
}
return true;
}
}
丢失的数字
算法思路:
可以用哈希表和高斯求和, 我们这里用位运算
如果我们把数组中的数,以及 [0, n] 中的数全部「异或」在⼀起,那么根据「异或」运算的两个相同的数异或的结果一定为0,最终的异或结果就是缺失的数
class Solution {
public int missingNumber(int[] nums) {
int ret = 0;
for(int i = 0; i < nums.length; i++) ret ^= nums[i];
for(int i = 0; i <= nums.length; i++) ret ^= i;
return ret;
}
}
两整数之和
算法思路:
- 异或 ^ 运算本质是「⽆进位加法」;
- 按位与 & 操作能够得到「进位」;
- 然后⼀直循环,直到「进位」变成 0 为⽌。
class Solution {
public int getSum(int a, int b) {
while(b != 0) {
int tmp = (a & b) << 1;//需要进位
a ^= b;//无需进位
b = tmp;
}
return a;
}
}
递归:
class Solution {
public int getSum(int a, int b) {
if(b == 0) return a;
return getSum(a^b, (a&b)<<1);
}
}
只出现⼀次的数字
class Solution {
public int singleNumber(int[] nums) {
int ret = 0;
for(int x: nums) ret ^= x;
return ret;
}
}
只出现⼀次的数字II
算法思路(比特位计数):
设要找的数为 ret 。由于整个数组中,需要找的元素只出现了「⼀次」,其余的数都出现了「三次」,因此我们可以根据所有数的「某⼀个比特位」的总和 %3 的结果,快速定位到 ret 那位「比特位」的值是0 还是 1 。
这样,我们通过 ret 每⼀个比特位上的值,就可以将 ret 还原出来。
class Solution {
public int singleNumber(int[] nums) {
int ret = 0;
for(int i = 0; i < 32; i++) { //依次修改ret中的每一个比特位
int sum = 0;
for(int x: nums) //统计 nums 中所有的数的第 i 位的和
if(((x >> i) & 1) == 1)
sum++;
sum %= 3;
if(sum == 1) ret |= 1 << i;
}
return ret;
}
}
消失的两个数字
算法思路:
只要是不同的数,异或后肯定有1, 1的那个比特位不相同,所以我们可以把它们分成两组
过程:
1.全部异或在一起
2.找出两个数不同的比特位
3.分组异或
class Solution {
public int[] missingTwo(int[] nums) {
//1.先把所有数异或在一起
int tmp = 0;
for(int x: nums) tmp ^= x;
for(int i = 1; i <= nums.length + 2; i++) tmp ^= i;
//2.找出两个数不同的比特位
int diff = 0;
while(true) {
if(((tmp >> diff) & 1) == 1) break;
else diff++;
}
//3.将数组的数和[1, n + 2] 区间内所有数按照diff位不同,分两类异或
int[] ret = new int[2];
for(int x: nums) {
if(((x >> diff) & 1) == 1) ret[1] ^= x;
else ret[0] ^= x;
}
for(int i = 1; i <= nums.length + 2; i++) {
if(((i >> diff) & 1) == 1) ret[1] ^= i;
else ret[0] ^= i;
}
return ret;
}
}
数组变换
先找出最大的数,然后遍历其他数,判断一直乘2能否变成最大那个数
import java.util.Scanner;
public class Main {
static int maxNums = 0;//记录最大的数
static boolean check(int x) {
while (x <= maxNums) {
x *= 2;
if (x == maxNums) return false;
}
return true;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = in.nextInt();
if (arr[i] > maxNums) maxNums = arr[i];
}
int i = 0;
for (; i < n; i++) {
if (arr[i] == maxNums) continue;
if (arr[i] % 2 == 0 && check(arr[i])) break;
}
if (i == n) System.out.print("YES");
else System.out.print("NO");
}
}
位运算,先找到最大的数,遍历其他数。判断最大的数/其他数 是否是2的次方, 判断方式有两种位运算的方式
- (x & (x-1)) != 0
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] arr = new int[n];
int maxNums = 0;
for(int i = 0; i < n; i++) {
arr[i] = in.nextInt();
maxNums = Math.max(maxNums , arr[i]);
}
boolean flag = true;
for(int i = 0; i < n; i++) {
if(maxNums % arr[i] != 0) {
flag = false;
break;
}
int x = maxNums / arr[i];
if((x & (x-1)) != 0) {
flag = false;
break;
}
}
if(flag) System.out.println("YES");
else System.out.println("NO");
}
}
- (x - (x & -x)) != 0
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] arr = new int[n];
int maxNums = 0;
for(int i = 0; i < n; i++) {
arr[i] = in.nextInt();
maxNums = Math.max(maxNums , arr[i]);
}
boolean flag = true;
for(int i = 0; i < n; i++) {
if(maxNums % arr[i] != 0) {
flag = false;
break;
}
int x = maxNums / arr[i];
if((x - (x & -x)) != 0) {
flag = false;
break;
}
}
if(flag) System.out.println("YES");
else System.out.println("NO");
}
}