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


文章目录

摘要

这题的名字叫「数组中两个数的最大异或值」,看起来像是个普通的数学题,

但实际上这是个经典的位运算 + 前缀树优化问题

直观暴力法就是两两异或一遍再取最大值,

但那样是 O(n²) 的复杂度,数组长度一大直接炸掉。

而高效做法是用「Trie(前缀树)」按二进制位 去存每个数,

然后每次在树里找一个最能让当前数异或结果更大的路径

最后就能在 O(32n) 的复杂度内解决问题。

本文我们会用 Swift 实现一个完整的解法,并带上运行示例与实战分析。

描述

题目要求:

给定一个整数数组 nums,返回数组中任意两个数 nums[i] XOR nums[j] 的最大值。

其中异或运算(^)的规则是:

  • 相同为 0,不同为 1。

举个例子
5 (0101)25 (11001) 的异或是:

txt 复制代码
0101
11001
-----
11100 (28)

结果就是 28。

所以题目等价于:

找到一对数字,它们在二进制每个位上尽量不同。

题解答案

这题有两种常见思路

暴力法

两层循环,枚举所有 (i, j) 组合,取最大 nums[i] ^ nums[j]

复杂度 O(n²),当 n = 2×10⁵ 时,完全不可行。

字典树法(Trie)

  • 把每个数字的二进制表示插入到一棵二叉 Trie 树中(0/1 分支);
  • 对每个数,从高位到低位尝试在 Trie 中找「相反位」;
  • 这样就能最大化异或结果。

我们选择第二种方法,效率高又优雅。

题解代码分析

下面是完整的 Swift 版本(可以直接在 Xcode Playground 运行)

swift 复制代码
import Foundation

class TrieNode {
    var children: [Int: TrieNode] = [:]
}

class Solution {
    func findMaximumXOR(_ nums: [Int]) -> Int {
        let root = TrieNode()
        
        // 将数字插入Trie
        func insert(_ num: Int) {
            var node = root
            for i in stride(from: 31, through: 0, by: -1) {
                let bit = (num >> i) & 1
                if node.children[bit] == nil {
                    node.children[bit] = TrieNode()
                }
                node = node.children[bit]!
            }
        }
        
        // 查找最大异或
        func query(_ num: Int) -> Int {
            var node = root
            var xorValue = 0
            for i in stride(from: 31, through: 0, by: -1) {
                let bit = (num >> i) & 1
                // 想异或最大,就尽量找相反位
                let toggledBit = 1 - bit
                if let next = node.children[toggledBit] {
                    xorValue |= (1 << i)
                    node = next
                } else if let next = node.children[bit] {
                    node = next
                }
            }
            return xorValue
        }
        
        // 主流程
        var maxXor = 0
        for num in nums {
            insert(num)
        }
        for num in nums {
            maxXor = max(maxXor, query(num))
        }
        return maxXor
    }
}

代码逐步解析

① TrieNode 定义
swift 复制代码
class TrieNode {
    var children: [Int: TrieNode] = [:]
}

每个节点最多有两个分支:

  • children[0] 表示当前位是 0
  • children[1] 表示当前位是 1
② 插入数字到 Trie
swift 复制代码
for i in stride(from: 31, through: 0, by: -1) {
    let bit = (num >> i) & 1
    ...
}

我们把每个数的 32 位二进制从高到低依次插入,

确保 Trie 里保留所有数字的二进制路径。

举例:插入 5 (000...0101) 时:

txt 复制代码
root
 └─0
    └─0
      ...
       └─1
         └─0
           └─1
③ 查找最大异或结果
swift 复制代码
let toggledBit = 1 - bit
if let next = node.children[toggledBit] {
    xorValue |= (1 << i)
    node = next
}

遍历当前数字时,

  • 如果能走相反的分支(1-bit),就走那边(这样该位异或能得到 1);
  • 否则只能走相同的分支。

这就是让异或值尽量变大的贪心策略。

④ 遍历所有数字求最大值
swift 复制代码
for num in nums {
    maxXor = max(maxXor, query(num))
}

对每个数字都查一遍,找到能和它异或最大的结果。

示例测试及结果

我们来跑几个示例

swift 复制代码
let solution = Solution()
print(solution.findMaximumXOR([3,10,5,25,2,8]))     // 输出: 28
print(solution.findMaximumXOR([14,70,53,83,49,91,36,80,92,51,66,70])) // 输出: 127
print(solution.findMaximumXOR([0]))                 // 输出: 0
print(solution.findMaximumXOR([2,4]))               // 输出: 6

输出结果

txt 复制代码
28
127
0
6

可以看到完全符合题目预期。

例如 [3,10,5,25,2,8] 中,5 ^ 25 = 28 确实是最大异或值。

时间复杂度

  • 构建 Trie:O(n × 32)
  • 查询每个数最大异或值:O(n × 32)

总时间复杂度:O(32n) ≈ O(n)

空间复杂度

Trie 树的节点最多为 32 × n 个。
空间复杂度:O(32n) ≈ O(n)

总结

这道题是「位运算 + Trie 树」的经典题型。

它的核心思想是:

利用二进制前缀树在每一位上做"相反匹配",

让异或的每一位都尽量为 1,从而得到最大值。

在实际开发中,这种思想常用于:

  • 网络掩码匹配(IP 地址的前缀匹配);
  • 路由查找;
  • 加密或压缩算法中位优化。

Swift 实现虽然看起来略繁琐,但逻辑其实非常清晰:

结构体化思维 + 位运算配合字典树,是一种非常高效且优雅的解法。

相关推荐
cici158741 小时前
基于高光谱成像和偏最小二乘法(PLS)的苹果糖度检测MATLAB实现
算法·matlab·最小二乘法
StarPrayers.3 小时前
自蒸馏学习方法
人工智能·算法·学习方法
大锦终3 小时前
【动规】背包问题
c++·算法·动态规划
智者知已应修善业3 小时前
【c语言蓝桥杯计算卡片题】2023-2-12
c语言·c++·经验分享·笔记·算法·蓝桥杯
hansang_IR4 小时前
【题解】洛谷 P2330 [SCOI2005] 繁忙的都市 [生成树]
c++·算法·最小生成树
Croa-vo4 小时前
PayPal OA 全流程复盘|题型体验 + 成绩反馈 + 通关经验
数据结构·经验分享·算法·面试·职场和发展
AndrewHZ4 小时前
【图像处理基石】 怎么让图片变成波普风?
图像处理·算法·计算机视觉·风格迁移·cv
无极小卒4 小时前
如何在三维空间中生成任意方向的矩形内部点位坐标
开发语言·算法·c#
FMRbpm5 小时前
链表中出现的问题
数据结构·c++·算法·链表·新手入门