力扣刷题记录--位运算问题

这里写目录标题

  • 一、n&(n-1)
    • 1. 求一个数的二进制表示中的1的个数
      • 力扣 191. 位1的个数
      • AcWing 801. 二进制中1的个数
    • 2. 判断一个数是否是2的方幂
  • 二、n&(~n+1)或n&-n
      • AcWing 801. 二进制中1的个数
  • 三、n>>k&1
  • 四、 ^ 操作(异或)
    • 1.交换两个数
      • LeetCode 344. 反转字符串
      • LeetCode 189. 轮转数组


一、n&(n-1)

n&(n-1)作用将n的二进制表示中的最低位为1的改为0。

一个简单的例子:

  • n = 10100(二进制),最低位的1是10100(加粗下划线处),
  • n-1 = 10011 ,为了能够-1,n的二进制位需要向其最低位的1借位,所以n-1会将最低位的1变成0。

可能会有人问:那10011(n-1)不是比10100(n)还多弄出了一个1吗?所以这时候就需要“&”运算的参与了。“n-1”所造成的多出的1,在和“n”相“&”之后,一定会被消除为0,因为其之所以能多出1,一定是因为原先的“n”在对应位置上为0,借位不得而成的1。

  • n&(n-1) = 10000
    可以看到原本最低位为1的那位变为0。

应用:

1. 求一个数的二进制表示中的1的个数

while(n>0){
	count ++;
    n&=(n-1);
}

例题:

力扣 191. 位1的个数

原题链接

力扣刷题记录--位运算问题_第1张图片

代码如下

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int count=0;
        while(n!=0){
            n&=(n-1);
            count++;
        }
        return count;
    }
}

AcWing 801. 二进制中1的个数

原题链接
力扣刷题记录--位运算问题_第2张图片
代码如下:

import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc =new Scanner(System.in);
        int n=sc.nextInt();
        int[] a=new int[n];
        for(int i=0;i<n;i++)a[i]=sc.nextInt();
        
        for(int i=0;i<n;i++){
            int count=0;
            while(a[i]>0){
                a[i]&=a[i]-1;
                count++;
            }
            a[i]=count;
        }
        for(int i=0;i<n;i++){
            System.out.print(a[i]+" ");
            
        }
    }
}

2. 判断一个数是否是2的方幂

n > 0 && ((n & (n - 1)) == 0 )

解释((n & (n-1)) == 0):

如果A&B==0,表示A与B的二进制形式没有在同一个位置都为1的时候。

那么本题到底啥意思??

不妨先看下n-1是什么意思。

令:n=1101011000(二进制,十进制也一样),则

n-1=1101010111。

n&(n-1)=1101010000

由此可以得出,n和n-1的低位不一样,直到有个转折点,就是借位的那个点,从这个点开始的高位,n和n-1都一样,如果高位一样这就造成一个问题,就是n和n-1在相同的位上可能会有同一个1,从而使((n & (n-1)) != 0),如果想要

((n & (n-1)) == 0),则高位必须全为0,这样就没有相同的1。

所以n是2的幂或0。


二、n&(~n+1)或n&-n

n&-n或n&(~n+1)的作用: 保留二进制下最后出现1的位置的数字,其余位置置0;

例题:

AcWing 801. 二进制中1的个数

原题链接
力扣刷题记录--位运算问题_第3张图片
代码如下:


import java.util.Scanner;

public class Main{
    // lowbit函数只保留n的二进制最低位的1
    public static int lowbit(int n){
        return n&-n;
        //return n&((~n)+1);
    }
    public static void main(String[] args){
        Scanner sc =new Scanner(System.in);
        int n=sc.nextInt();
        int[] a=new int[n];
        for(int i=0;i<n;i++)a[i]=sc.nextInt();
        
        for(int i=0;i<n;i++){
            int count=0;
            while(a[i]>0){
                a[i]-=lowbit(a[i]);//把二进制最低位的1减去,能减多少次,其二进制就有多少个1
                count++;
            }
            a[i]=count;
        }
        for(int i=0;i<n;i++){
            System.out.print(a[i]+" ");
            
        }
    }
}

三、n>>k&1

n>>k&1的作用:可以算出n的二进制表示中,从低到高第k位是0还是1(最低位为第0位)。

举例:n=10,二进制表示:1010,假设求第3位(从0开始)是多少?

n>>3=0001,0001&1=1,所以第三位是1(1010)


四、 ^ 操作(异或)

1.交换两个数

原理:

  • a^a=0;
  • a^0=a;
  • a^b = b^a;交换律
  • a ^b ^c =a^ (b^ c)=(a ^b )^ c;结合律
  • 所以a^ b^a=b。

LeetCode 344. 反转字符串

原题链接

对字符数组内容进行翻转,左右指针向中间遍历,交换首尾指针指向的字符。交换采用异或操作,可以不使用额外变量。

代码如下:

class Solution {
    public void reverseString(char[] s) {
         int l=0,r=s.length-1;
         while(l<r){
             s[l]^=s[r];//s[l]=s[l]^s[r]
             s[r]^=s[l];//s[r]=s[r]^s[l]=s[r]^(s[l]^s[r])=s[l]
             s[l]^=s[r];//s[l]=s[l]^s[r]=(s[l]^s[r])^s[l]=s[r]
             l++;
             r--;
         }
    }
}

LeetCode 189. 轮转数组

原题链接

2023.06.03 二刷

题解区里看到有人引用国外的一个短小精悍的题解:
示例:
nums = “----->–>”; k =3
result = “–>----->”;

过程
reverse “----->–>” we can get “<–<-----”
reverse “<–” we can get “–><-----”
reverse “<-----” we can get “–>----->”

代码如下:

class Solution {
    public void rotate(int[] nums, int k) {
        int n=nums.length;
        k%=n;//k可能比nums大,但是nums右移n位还是原来的nums
        reverse(nums,0,n-1);//反转区间两端都为闭
        reverse(nums,0,k-1);
        reverse(nums,k,n-1);
    }
    //对数组指定区间进行反转
    public void reverse(int[] nums,int l,int r ){
        while(l<r){
            // 基于异或运算的交换律和结合律,以及a^a=0,a^0=a;
            nums[l]^=nums[r];//nums[l]=nums[l]^nums[r]
            nums[r]^=nums[l];//nums[r]=nums[r]^nums[l]^nums[r]=nums[l]
            nums[l]^=nums[r];//nums[l]=(nums[l]^nums[r])^nums[l]=nums[r]
            l++;
            r--;
        }
    }
}

你可能感兴趣的:(力扣刷题记录,leetcode,java,算法)