Browse curriculum

Maximum XOR of Two Numbers in an Array

Find the maximum XOR of any two numbers in an array by storing every number's bits in a trie and greedily walking toward the opposite bit at each level.

Problem Understanding

Maximum XOR of Two Numbers in an Array (LeetCode 421): given non-negative integers nums, return the maximum value of nums[i] XOR nums[j] over all pairs.

Examples: [3,10,5,25,2,8] → 28 (5 XOR 25); [14,70,53,83,49,91,36,80,92,51,66,70] → 127.

The Intuition: Win the Highest Bit First

Checking every pair is O(n²). A trie of bits answers "what is the best partner for this number?" in one pass down:

  1. Store bits, highest first. Each path from the root to a leaf is one number.
  2. Walk against your own bits. At each level, prefer the child with the opposite bit; that bit of the XOR becomes 1.
  3. Fall back only when forced. If the opposite child is missing, the same bit is the only way on, and that XOR bit is 0.

The same trie mechanics are built from scratch in Implement Trie, and XOR's cancelling behaviour is the core of Single Number.

Interactive Walkthrough

ONE NODE PER BIT, HIGHEST FIRST · NUMBER ABOVE A LEAF

BUILD THE TRIE

number

:

xor so far

:

best

0

 

6 numbers, written in 5 bits. First, every number goes into a trie of its bits

Next

Insert 3 = 00011: one node per bit, highest bit first

Free preview: the first 14 of 44 steps

nums

The trie fills first, one number at a time. Then each number walks it, taking the opposite bit wherever it can; the leaf it reaches is its best partner. Try Spread out, where one pair differs in every bit.

Dry Run Table

Input: [3,10,5,25,2,8], 5 bits.

Number Best partner XOR
3 (00011) 25 26
10 (01010) 25 19
5 (00101) 25 28
25 (11001) 5 28
2 (00010) 25 27
8 (01000) 25 17

Answer: 28.

The Solution Template

Build the bit trie, then one greedy walk per number.

Maximum XOR of Two Numbers in an Array Code
1var findMaximumXOR = function(nums) {
2 const bits = Math.max(Math.max(...nums).toString(2).length, 1);
3 const root = {};
4 for (const v of nums) {
5 let node = root;
6 for (let k = bits - 1; k >= 0; k--) {
7 const b = (v >> k) & 1;
8 node = node[b] ??= {};
9 }
10 }
11 let best = 0;
12 for (const v of nums) {
13 let node = root, x = 0;
14 for (let k = bits - 1; k >= 0; k--) {
15 const b = (v >> k) & 1;
16 if (node[1 - b]) {
17 x |= 1 << k;
18 node = node[1 - b];
19 } else {
20 node = node[b];
21 }
22 }
23 best = Math.max(best, x);
24 }
25 return best;
26};

Edge Cases & Common Mistakes

  • One number: its only pair is itself, so the answer is 0.
  • All numbers equal: every walk is forced, answer 0.
  • Inserting low bits first: the greedy choice must start at the most significant bit.
  • Querying before inserting everything: fine if you insert then query each number in turn, but the partner must already be in the trie.
  • Zero values: bit length of 0 is 0; use at least one bit.

Summary

Insert every number into a binary trie, highest bit first; for each number walk toward the opposite bit whenever possible. The largest XOR built by any walk is the answer. O(n · B) time and space.

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Build the trie

    Insert every number bit by bit, from the highest bit to bit 0.

  2. Walk greedily

    For each number, at every bit move to the opposite bit's child if it exists (that XOR bit is 1), otherwise follow the same bit (XOR bit 0).

  3. Keep the best

    The XOR built by each walk is that number's best pair; the maximum over all numbers is the answer.

Frequently asked questions

Why is the greedy walk correct?

A 1 in bit k of the XOR is worth 2^k, which is more than all lower bits together (2^k − 1). So whenever some number lets the XOR have a 1 in the highest undecided bit, choosing it can never be beaten by any choice in lower bits. The trie tells you instantly whether such a number exists: the child with the opposite bit.


Why insert the bits highest first?

The greedy decision must be made from the most significant bit down, so the trie is keyed in that order. Each level of the trie is one bit position, and the walk settles one XOR bit per level.


Do I need all 31 bits?

Only as many as the largest number uses. LeetCode values fit in 31 bits, so many solutions hard-code 31; using the bit length of the maximum gives the same answer with shorter paths.


Is there a way without a trie?

Yes: build the answer bit by bit with a hash set of prefixes. For each bit from the top, guess that the answer has a 1 there and check whether two prefixes XOR to the guess (a two-sum style lookup in the set). It is also O(n · B).

New to the technique behind this problem? Watch it run step by step:

Trie (Prefix Tree) Visualization

Applying for software engineering roles? Free, no sign-up:

AI resume checker: match your resume to the job description