0s表示一串0,1s表示一串1,^表示的异或操作,相同为0,不同为1
x ^ 0s = x x & 0s = 0 x | 0s = x
x ^ 1s = ~x(表示x取反操作) x & 1s = x x | 1s = 1s
x ^ x = 0 x & x = x x | x = x
利用 x ^ 1s = ~x 的特点,可以将一个数的位级表示翻转;
利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数:
1^1^2 = 2
利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作
一个数 num 与 mask:00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。
01011011 &
00111100
--------
00011000
利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。
一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。
01011011 |
00111100
--------
01111111
正整数的补码是其二进制表示,与原码相同。
负数补码是:将其原码除符号位外的所有位取反(0变1,1变0,符号位为1不变)后加1
数0的补码表示是唯一的 00000000
n&(n-1) 去除掉 n 的位级表示中 最低的那一位 1。例如对于二进制表示 01011011,减去 1 得到 01011010,这两个数相与得到 01011010。
01011011 &
01011010
--------
01011010
n&(-n) 得到 n 的位级表示中最低的那一位 1。-n 得到 n 的反码加 1,也就是 -n=~n+1。例如对于二进制表示 10110100,-n 得到 01001100,相与得到 00000100。
10110100 &
01001100
--------
00000100
n-(n&(-n)) 则可以去除 n 的位级表示中最低的那一位 1,和 n&(n-1) 效果一样。
">>>"无符号右移 操作规则:无论正负数,前面补零。
">>"右移 操作规则:正数前面补零,负数前面补1
"<<"左移 操作规则:无论正负数,后面补零。
右移、左移、无符号右移学习借鉴链接
要获取 111111111,将 0 取反即可,~0。
要得到只有第 i 位为 1 的 mask,将 1 向左移动 i-1 位即可,1<<(i-1) 。例如 1<<4 得到只有第 5 位为 1 的 mask :00010000。
要得到 1 到 i 位为 1 的 mask,(1<<i)-1 即可,例如将 (1<<4)-1 = 00010000-1 = 00001111。
要得到 1 到 i 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 ~((1<<i)-1)。
static int Integer.bitCount(); // 统计 1 的数量
static int Integer.highestOneBit(); // 获得最高位
static String toBinaryString(int i); // 转换为二进制表示的字符串
1.两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给出两个整数 x 和 y,计算它们之间的汉明距离。
public static int hammingDistance(int x, int y) {
int z=x^y;
int count=0;
while(z!=0){
if((z&1)==1){
count++;
}
z=z>>1;
}
return count;
}
public static int hammingDistance(int x, int y) {
int z=x^y;
return Integer.bitCount(z);
}
2.一个数组中只出现一次的数字
public int singleNumber(int[] nums) {
int ans=0;
for(int i=0;i<nums.length;i++){
ans=ans^nums[i];
}
return ans;
}
3.给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
异或满足结合律,对一个数进行两次完全相同的异或运算会得到原来的数,
因此我们可以通过异或运算找到缺失的数字。
public static int missingNumber(int[] nums) {
int n=nums.length;
for(int i=0;i<nums.length;i++){
n=n^i^nums[i];
}
return n;
}
4.给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
两个不相等的元素在位级表示上必定会有一位存在不同。
将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。
public int[] singleNumber(int[] nums) {
int ans=0;
for(int i=0;i<nums.length;i++){
ans=ans^nums[i];//获得只出现一次的两个元素的异或
}
int diff=0;
diff=ans&(-ans);
int []answer=new int[2];
for(int i=0;i<nums.length;i++){
if((nums[i]&diff)==0 ){
answer[0]=answer[0]^nums[i];
}else{
answer[1]=answer[1]^nums[i];
}
}
return answer;
}
5.翻转一个数的比特位
//考虑如果这个数的比特位很多 也就是翻转函数需要多次调用,我们将int转化成4byte来计算
private static HashMap<Byte,Integer>cache=new HashMap<>();//存储每个byte翻转之后的结果,如果存在byte翻转前相同的就不用再次计算了
public static int reverseBits(int n){
int ans=0;//暂时保留翻转之后的结果
for(int i=0;i<4;i++){
ans<<=8;//每一运算是一个字节 8位,所以需要向左移动8位,为保留后面一个字节翻转的结果腾地方
ans|=reverseByte((byte)(n&0b11111111));//0b表示后面是二进制数字 也就是截了n末尾8位进行翻转
n>>>=8;//把后八位删除掉
}
return ans;
}
public static int reverseByte(byte b){
if (cache.containsKey(b)) {
return cache.get(b);
}
int ans=0;
byte t=b;
for(int i=0;i<8;i++){
ans<<=1;
ans|=t&1;
t>>>=1;
}
cache.put(b,ans);
return ans;
}
6.不用额外变量交换两个数字
a=a^b; b=a^b; a=a^b;
7.判断一个数是不是2的幂次
public boolean isPowerOfTwo(int n) {
//return n>0&&Integer.bitCount(n)==1;
return n>0&&(n&(n-1))==0;//1000 & 0111=0
}
8.判断一个数是不是 4 的 n 次方
这种数在二进制表示中只有一个1,并且在奇数位置上出现
public boolean isPowerOfFour(int num) {
return num > 0 && (num & (num - 1)) == 0 && (num & 0b01010101010101010101010101010101) != 0;
}
9.给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。
满足交替位二进制意味着右移一位后与之间的数字作异或,每一位都是1
1010^101=1111
然后再判断每个位置上是否都是1 如果都是就是交替二进制数
public boolean hasAlternatingBits(int n) {
int ans=n^(n>>1);
return (ans&(ans+1))==0;
}
10.求一个数的补码
具体方法:00000101可以和00000111进行异或获得补码 所以重点是怎么获得后者
public static int findComplement(int num) {
if(num==0){
return 1;
}
int mask=1;
mask=mask<<30;
while((mask&num)==0){
//找到左侧数第一个1
mask=mask>>1;
}
mask=(mask<<1)-1;//00000100--00001000--00000111
return num^mask;
}
对于 10000000 这样的数要扩展成 11111111,可以利用以下方法:
mask |= mask >> 1 11000000
mask |= mask >> 2 11110000
mask |= mask >> 4 11111111
public int findComplement(int num) {
int mask = num;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;
return (mask ^ num);
}
11.实现整数的加法
a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。
递归会终止的原因是 (a & b) << 1 最右边会多一个 0,
那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。
public int getSum(int a, int b) {
return b==0?a:getSum((a^b),(a&b)<<1);
}
12.给定一个字符串数组 words,找到 length(word[i]) * length(word[j]) 的最大值,并且这两个单词不含有公共字母。你可以认为每个单词只包含小写字母。如果不存在这样的两个单词,返回 0。
public static void main(String[] args) {
String []strs={
"abcw","baz","foo","bar","xtfn","abcdef"};
System.out.println(maxProduct(strs));
}
public static int maxProduct(String[] words) {
int n=words.length;
int []val=new int[n];
for(int i=0;i<n;i++){
//遍历每个字符串
for(char c:words[i].toCharArray()){
//遍历字符串中的每个字符
val[i]|=1<<(c-'a');//这样来看abc这个字符串的val就是111 efg的val是111000
}
}
int ans=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if((val[i]&val[j])==0){
//说明没有一个字母相同
ans=Math.max(ans,words[i].length()*words[j].length());
}
}
}
return ans;
}
13.统计从 0 ~ n 每个数的二进制表示中 1 的个数
对于数字 6(110),它可以看成是 4(100) 再加一个 2(10)
所以将6最右侧的1去掉得到的十进制数字4,他的二进制表示中1的个数+1=6的二进制表示中1的个数
public int[] countBits(int num) {
int []ans=new int[num+1];
for(int i=1;i<=num;i++){
ans[i]=ans[i&(i-1)]+1;
}
return ans;
}