LeetCodeDiary

A Diary for solving LeetCode problems

View on GitHub

421. 数组中两个数的最大异或值

给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n

进阶:你可以在 O(n) 的时间解决这个问题吗?

示例 1:

输入:nums = [3,10,5,25,2,8]
输出:28
解释:最大运算结果是 5 XOR 25 = 28.

示例 2:

输入:nums = [0]
输出:0

示例 3:

输入:nums = [2,4]
输出:6

示例 4:

输入:nums = [8,10,2]
输出:10

示例 5:

输入:nums = [14,70,53,83,49,91,36,80,92,51,66,70]
输出:127

提示:

位运算 中等 每日一题

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

题解

解题思路

这道题目还是蛮有意思的,用到前缀树的思想来将复杂度降低到 O(N)。 我们分两步来解决这个问题:

  1. 构建二进制前缀树 具体来说就是利用数的二进制表示,从高位到低位构建一棵树(因为只有01 两个值,所以是一棵二叉树),每个从根节点到叶子节点的路径都表示一个数。(构建的树看下图)
  2. 搜索前缀树 然后遍历数组中的数字,将每一个二进制位,在对应的层中找到一个与或的最大值,也就是:如果是1,找0的那条路径,如果是0,找1的那条路径。 这样搜索下来的路径就是这个数字和整个数组与或的最大值,看下图 tree.jpg

具体步骤是: 对于2, 二进制从高到低是 0,0,1,0

所以我们与或的结果是1010, 十进制表示是10.

分析:

其实每个数字还是都要和其他数字做个异或,不过利用了前缀树和贪心思想,每一步都挑一个最大的值,把其他路径都剪枝掉了。

复杂度分析

总体复杂度为 O(N)

代码

class TrieNode:
    def __init__(self):
        self.children = defaultdict(TrieNode)

class Trie:
    def __init__(self, nums):
        self.root = TrieNode()
        for n in nums:
            self.insert(n)
    
    def insert(self, n): # 从右向左每一位插入Trie
        cur = self.root
        for i in range(31, -1, -1):
            cur = cur.children[(n>>i) & 1]
    
    def searchMax(self, n):
        res, cur = 0, self.root
        for i in range(31, -1, -1):           # 从右向左找最多xor=1的儿子
            bit = (n>>i) & 1
            if bit ^ 1 in cur.children:        # 找到就向儿子走
                cur = cur.children[bit ^ 1]
                res += (1<<i)
            else:                  # 没找到就向bit走,因为有可能之后会找到xor=1的儿子
                cur = cur.children[bit]
        return res

class Solution:
    def findMaximumXOR(self, nums: List[int]) -> int:
        t = Trie(nums)
        return max([t.searchMax(n) for n in nums])