【问题标题】:Maximum XOR of Two Numbers in an Array and Bitmasking数组中两个数字的最大异或和位掩码
【发布时间】:2021-06-23 06:49:40
【问题描述】:

LC:https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/

public int findMaximumXOR(int[] nums) {
        
     int max = 0, mask = 0;
        
     for (int i = 31; i >= 0; i--){
            mask = mask | (1 << i);
            
            Set<Integer> set = new HashSet<>();
            
            for(int num : nums){
                int left = num & mask
                set.add(left);
            }
            int greed = max | (1 << i);
            
            for (int prefix : set){
                if (set.contains(greed ^ prefix)) {
                    max = greed;
                    break;
                }
            }
        }
        return max;
    }

有人能解释一下当我们在 nums[i] 上应用 AND 运算符时会发生什么,而且掩码似乎越来越小(从 2^31、2^30...开始)

int left = num & mask

我从 cmets 知道它应该保留左侧位而忽略右侧位,但我仍然不确定这段代码背后发生了什么。

【问题讨论】:

    标签: algorithm bit-manipulation bitmask bitwise-and


    【解决方案1】:

    给定输入[3,10,5,25,2,8],输出为28(由5^25给出):

       Num   Binary
        5     00101
     ^ 25     11001
       ------------
     = 28     11100
    

    请注意,xor 运算在两个位不同时给出1,否则为0。因此,考虑到这一点,我们从左边开始(最重要的位,由您的代码中的i=31 给出)。

    mask = mask | (1 << i);
    

    我们在每次迭代中计算mask。在第一次迭代中,掩码是100...000,然后是1100...000,以此类推。如果您不确定如何使用,请参考this answer

    请注意,我们使用的是贪婪方法 - 当您处理 ith 位时,您只关心该位置的位;左边的那些之前已经被处理过了,右边的那些将在随后的迭代中被处理。考虑到这一点,在下一步中,我们创建一个哈希集 set 并将所有 num &amp; mask 值放入其中。例如,当 i=30 时,我们的掩码是 110...000,所以此时,我们只查看 525 中的 i=30th 位(左侧的 MSB,在 i=31,已经已经处理好了,因为我们做了&amp;,所以右边的那些会被忽略,因为我们的面具都有0s)。

    接下来,用:

    int greed = max | (1 << i);
    

    我们为当前迭代设置了“期望”。理想情况下,我们希望在ith 位置有一个1,因为我们想找到最大的异或。有了这个集合,我们查看set 中的所有元素,看看是否有任何一个符合我们的期望。如果我们找到了,那么我们相应地更新我们的max,否则我们的max 保持不变。

    【讨论】:

    • 谢谢!鉴于您上面的输入数组 [3,10,5,25,2,8] 我们知道在某些时候,我们将计算贪婪值 = 28,因为这是数组中的最大 XOR。在某些时候,Greed(28) ^ left(4) = 24 已经存在于 Set 中,这就是我们设置 max = 28 的原因。通过在 num & mask 中使用 AND 运算符,我们得到一个可用于 XOR 的数字贪婪地确定结果数字是否存在于集合中。我在概念上难以理解的是num &amp; mask(左)如何以及为什么会以它的方式工作?
    • @MikeChan,如果我理解正确,我们事先并不知道我们会计算贪婪值 28。我们只是设定了我们的期望,并尝试看看我们的期望是否得到满足。 num&amp;mask 所做的只是将我们的注意力集中在当前位上——在接下来的迭代中,左边的那些已经被处理了,而下面的则被研究了。
    【解决方案2】:

    我发现将其视为递归算法最容易。

    基本情况是所有数字都为零。那么最大异或显然为零。

    递归地,我们修剪掉每个数字的最低有效位并解决子问题。现在,鉴于我们知道这个子问题的最大值,称之为submax,答案是submax&lt;&lt;1(submax&lt;&lt;1)|1,因为数字的最低有效位只影响最大异或的最低有效位。我们可以通过测试任意两个数字是否有这个异或来判断答案是否为(submax&lt;&lt;1)|1

    此代码更喜欢屏蔽低位而不是移位。循环打开mask 的每个连续位,从最高有效到最低,对应于当前递归调用中数字长度的增加。 num &amp; mask 将当前忽略的低位清零。

    【讨论】:

    • 感谢您的回复!在 LC 上,海报说面具将从 100..000 、110..000、111..000 开始,然后是 1111...111 但是当我控制台注销时,地图似乎每次通过都会逐渐减半从 2^31 一直到 1。只是想知道,哪种方法是正确的?
    • @MikeChan 第一个。
    • 感谢大家的帮助!只是想问最后一件事:只是想知道我们何时计算 left 如下: int left = num & mask 这个特定或只是在某些时候我们确定某个 nums [ i] XOR 左将 === 贪婪? (这就是为什么我们不断修改它并将 1 放在第 i 个位置)
    • @MikeChan 我们不知道。我们知道答案是maxgreed。答案是 greed 当且仅当我们找到两个 XOR 为 greed 的数字(即,greed XOR 一个被屏蔽的数字包含在一组其他被屏蔽的数字中),在这种情况下,我们将 max 更新为贪心又显露另一位。
    猜你喜欢
    • 2010-11-12
    • 2012-03-08
    • 1970-01-01
    • 1970-01-01
    • 2013-12-03
    • 2016-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多