0
点赞
收藏
分享

微信扫一扫

在 Java 中,JDK、JRE、JVM 分别代表什么,有何关系和区别?

捌柒陆壹 2024-06-30 阅读 12

文章目录

    • 前置知识
    • 1. 交换两个数
    • 2. 比较两个数的大小
    • 3. leetcode268 寻找缺失的数字
    • 4. leetcode136 只出现一次的数字
    • 5. leetcode260 只出现一次的数字|||
    • 6. leetcode137 只出现一次的数字||
    • 7. 2/3的幂
    • 8. 大于等于该数字的最小2的幂
    • 9. leetcode201 数字范围按位与
    • 10. 位运算中分治法举例

前置知识

1. 交换两个数

因为我们的异或运算遵循的交换律和结合律, 所以我们可以写出下面的这一段代码

public void swap(int[] arr, int i, int j) {
        //注意这里的 i != j
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }

请注意这里面的 i != j 因为如果相等的话, 会直接把这一块区域置为0

2. 比较两个数的大小

/**
     * 不包含比较相关的逻辑运算符来得到两个数当中的最大值
     */
    //flip意为反转 --> 此时传入的n保证为 1 / 0 --> 如果是1, 就转化为0 , 如果是0, 就转化为1
    private int flip(int n) {
        return n ^ 1;
    }

    //获取一下某个数字的符号,如果是非负的数字就返回一个1, 如果是负数就返回0
    private int sign(int n) {
        return flip(n >>> 31);
    }

    //下面我们给出两种判断的方法 --> 第一种有可能会溢出, 第二种不会溢出
    //核心的逻辑就是设置 returnA , returnB 来规定什么时候返回a, 什么时候返回b(两个return一定是互斥的才可以)
    public int getMax1(int a, int b) {
        //c可能会溢出
        int c = a - b;
        int returnA = sign(c);
        int returnB = flip(returnA);
        return a * returnA + b * returnB;
    }
//下面的这个方法是不会溢出的
    public int getMax2(int a, int b) {
        //c依然可能会溢出
        int c = a - b;

        //下面判断一下a b c的符号
        int signA = sign(a);
        int signB = sign(b);
        int signC = sign(c);

        //判断a b 符号是不是相同的
        int diffAB = signA ^ signB; //不同返回1
        int sameAB = flip(diffAB); //相同返回1

        //什么时候进行a的返回 
        //--> 1. ab不同号且a为非负
        //--> 2. ab同号(此时不可能会溢出)且signC == 1
        int returnA = diffAB * signA + sameAB * signC;
        int returnB = flip(returnA);
        return a * returnA + b * returnB;
    }

上述的代码逻辑和之前的那个其实是一致的
就是加入了一些是否越界的判断(到底什么时候returnA)

3. leetcode268 寻找缺失的数字

在这里插入图片描述

class Solution {
    public int missingNumber(int[] nums) {
        //说白了这个题就是异或运算的性质
        int eor = 0;
        for(int elem : nums){
            eor ^= elem;
        }
        int eorN = 0;
        for(int i = 0; i <= nums.length; ++i){
            eorN ^= i;
        }
        return eorN ^eor;
    }
}

4. leetcode136 只出现一次的数字

在这里插入图片描述

class Solution {
    public int singleNumber(int[] nums) {
        int eor = 0;
        for(int element : nums){
            eor = eor ^ element;
        }
        return eor;
    }
}

5. leetcode260 只出现一次的数字|||

在这里插入图片描述

class Solution {
    public int[] singleNumber(int[] nums) {
        int eor = 0;
        for(int elem : nums){
            eor ^= elem;
        }
        int n = eor & (~eor + 1);
        int eorN = 0;
        for(int elem : nums){
            if((elem & n) == 0){
                eorN ^= elem;
            }
        }
        return new int[]{eorN, eor ^ eorN};
    }
}

6. leetcode137 只出现一次的数字||

在这里插入图片描述

class Solution {
    public int singleNumber(int[] nums) {
        //首先进行的是统计每一个数位上的1的个数
        int[] hash = new int[32];
        for(int elem : nums){
            for(int i = 0; i < 32; ++i){
                hash[i] = hash[i] + ((elem >>> i) & 1);
            }
        }

        //统计1的分位个数完毕, 开始还原数字
        int ans = 0;
        for(int i = 0; i < 32; ++i){
            if(hash[i] % 3 != 0){
                ans = ans | (1 << i);
            }
        }
        return ans;
    }
}

7. 2/3的幂

class Solution {
	/**
     * 判断一个数字是不是2的幂
     * @param n
     * @return
     */
    public boolean isPowerOfTwo(int n){
        return n > 0 && (n & (~n + 1)) == n;
    }

    /**
     * 判断一个数字是不是3的幂(直接找到int范围内3的最大的幂是多少
     * @param n
     * @return
     */
    public boolean isPowerOfThree(int n){
        return n > 0 && 1162261467 % n == 0;
    }

}

8. 大于等于该数字的最小2的幂

class Solution{
	public int nearTwoPower(int n){
        if(n <= 1){
            return 1;
        }
        n--;
        n = n | (n >>> 1);
        n = n | (n >>> 2);
        n = n | (n >>> 4);
        n = n | (n >>> 8);
        n = n | (n >>> 16);
        return n + 1;
    }
}

9. leetcode201 数字范围按位与

在这里插入图片描述

class Solution{
	public int rangeBitswiseAnd(int left,int right){
        while (left < right) {
            right = right - (right & (~right + 1));
        }
        return right;
    }
}

10. 位运算中分治法举例

class Solution{
	/**
     * 翻转二进制的状态
     * 比如一个数的二进制位是 : 0001101101010010 --reverse--> 010010101011000
     * 分析 : 正常的解法就是定义一个ans, 看到哪一位上有i你就或上去一个1就行了, 这里我们不在多说, 有点简单
     *       我们重点说一下位运算分治的思路(从两个一组翻转 --> 四个一组 --> 八个一组.....)
     *       假如有一个序列  a b c d e f g h
     *       我们翻转的过程: 1. b a d c f e h g (1v1翻转)
     *                    2. d c b a h g f e (2v2翻转)
     *                    3. h g f e d c b a (4v4翻转)
     *       下面推广到代码上 :
     *                   1. a b c d e f g h & 0 1 0 1 0 1 0 1  ==> 0 b 0 d 0 f 0 h (1)
     *                      a b c d e f g h & 1 0 1 0 1 0 1 0 ==>  a 0 c 0 e 0 g 0 (2)
     *                      (1) << 1 | (2) >>> 1  ==> b a d c f e h g
     *       下面的过程以此类推
     * @param n
     * @return
     */
    public int reverseBits(int n){
        n = ((n & 0x55555555) << 1) | ((n & 0xaaaaaaaa) >>> 1);
        n = ((n & 0x33333333) << 2) | ((n & 0xcccccccc) >>> 2);
        n = ((n & 0x0f0f0f0f) << 4) | ((n & 0xf0f0f0f0) >>> 4);
        n = ((n & 0x00ff00ff) << 8) | ((n & 0xff00ff00) >>> 8);
        n = ((n & 0x0000ffff) << 16) | ((n & 0xffff0000) >>> 16);
        return n;
    }


    /**
     * 计算一个数字的二进制位中有几个1
     * 还是用的位运算分治的方法
     *   思路分析 : 假如数字的二进制位是  1 0 1 1 0 1 0 1
     *
     * @param n
     * @return
     */
    public int cntOnes(int n){
        n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
        n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
        n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
        n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);
        n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff);
        return n;
    }
}
举报

相关推荐

0 条评论