【leetCode】 1829. 每个查询的最大异或值

284 阅读2分钟

这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战

题目

leetcode-cn.com/problems/ma…

关键的提示

1 <= maximumBit <= 20 0 <= nums[i] < 2^maximumBit^ nums 中的数字已经按 升序 排好序。

前置知识

  • 异或(XOR)的含义:

    • A XOR B在编程中一般以 A^B来表示
    • 含义是:A和B转化为二进制时,两位相同为0,不同为1,返回各位的结果。
  • 取反(or)的含义:

    • orB 一般表示为 ~B。
    • 含义是:将B中二进制的每一位改为不同的数。(0改为1,1改为0)

分析

既然A^B含义是两位不同为1相同为0,那么:

  • 所求的 :nums[0] XOR nums[1] XOR ... XOR nums[nums.length-1] XOR k 中的k

只要尽可能地将nums[0] XOR nums[1] XOR ... XOR nums[nums.length-1] 中的二进制结果按位取反即可,如果超出了题目指定的2^maximumBit的大小,那么我们覆盖可以覆盖的就可以了。

  • 同时,注意到数组会依次删除最后一个,那么其实倒数第n次的结果就包含前n个数,倒序放入数组。

  • 按位取反的运算符是~,同时注意到nums[i]<2^maximumBit^,那么直接取反,并截去前面大于maximumBit位的数就可以了。

  • 而截去前n位,一个简单的做法就是左移32-n位再右移32-n位。

编码

分析完毕,依葫芦画瓢,首先是取数的:

public static int getBinaryReverse(int resource,int bit){
    int tar = ~resource;
    //左移再右移,把多出来的位删掉
    tar <<= 32-bit;
    tar>>>= 32-bit;
    return tar;
}

随后是上层的:

public static int[] getMaximumXor(int[] nums, int maximumBit) {
    int n = nums.length;
    int[] res = new int[n];
    int xor = 0;
    for (int i = n-1; i >= 0; i--) {
        xor ^= nums[n-i-1];
        res[i] = getBinaryReverse(xor,maximumBit);
    }
    return res;
}

这里依次反着来,这样子每次XOR都只需要加上一个数。

这里就是:

  • O(n)的时间复杂度(数组每个元素都只访问了1次),
  • O(n)的空间复杂度(毕竟我们新建了一个数组)

当然这个空间复杂度我们可以用原来的数组来做(原地转换)利用到上面只访问一次的特性,填完之后把数组翻过来,毕竟题目没说不能改:

public static int[] getMaximumXor(int[] nums, int maximumBit) {
    int xor = 0;
    for (int i = 0; i < nums.length; i++) {
        xor ^= nums[i];
        nums[i] = getBinaryReverse(xor,maximumBit);
    }
    reverseArr(nums);
    return nums;
}

    public static void reverseArr(int[] nums){
        int left = 0 ,right = nums.length-1;
        while(left<=right){
            int tmp = nums[left];
            nums[left] = nums[right];
            nums[right] = tmp;
            left++;
            right--;
        }
    }

使用额外数组的结果:

执行用时:3 ms, 在所有 Java 提交中击败了54.74%的用户

内存消耗:53.4 MB, 在所有 Java 提交中击败了95.79%的用户

使用原地数组的结果:

执行用时:5 ms, 在所有 Java 提交中击败了30.53%的用户

内存消耗:50.7 MB, 在所有 Java 提交中击败了96.84%的用户

进一步分析

leetCode上更好的解法,实际上是对于上面位运算的一个改进:

public static int[] getMaximumXor(int[] nums, int maximumBit) {
        int n = nums.length;
        int cur = 0;
        int[] ans = new int[n];
        int mask = (1 << maximumBit) - 1;

        for(int i = 0; i < n; i++) {
            cur = cur ^ nums[i];
            ans[n - i - 1] = cur ^ mask;
        }

        return ans;
}

思考一下,其实没有必要取反再左右移动,直接上掩码即可。

  • 因为这里掩码必然每一位都会是1,那么这里异或一次就可以得到结果。