LeetCode 421 - Maximum XOR of Two Numbers in an Array


https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/
Given a list of numbers, a[0], a[1], a[2], … , a[N-1], where 0 <= a[i] < 2^32. Find the maximum result of a[i] XOR a[j].
Could you do this in O(n) runtime?

Input: [3, 10, 5, 25, 2, 8]
Output: 28

X.O(32N) + prefix
http://www.voidcn.com/article/p-zsinbvxx-hk.html
    public int findMaximumXOR(int[] nums) {
        int max = 0, mask = 0;
        
        // search from left to right, find out for each bit is there 
        // two numbers that has different value
        for(int i = 31; i >= 0; i--){
         
         // mask contains the bits considered so far
            mask = mask | (1 << i);
            Set<Integer> set = new HashSet<>();
            
            // store prefix of all number with right i bits discarded
            for(int num : nums){
                set.add(num & mask);// reserve Left bits and ignore Right bits
            }
            
            // now find out if there are two prefix with different i-th bit
            // if there is, the new max should be current max with one 1 bit at i-th position, which is candidate
            // and the two prefix, say A and B, satisfies:
            // A ^ B = candidate
            // so we also have A ^ candidate = B or B ^ candidate = A
            // thus we can use this method to find out if such A and B exists in the set 
            int tmp = max | (1 << i); //in each iteration, there are pair(s) whoes Left bits can XOR to max
            for(int prefix : set){
                if(set.contains(tmp ^ prefix)) {
                    max = tmp;
                    break;
                }
            }
        }
        return max;
    }
https://kingsfish.github.io/2017/12/15/Leetcode-421-Maximum-XOR-of-Two-Numbers-in-an-Array/
我们还需要用上一个异或的特性,假设ab产生了最终的答案max,即a ^ b = x,那么根据异或的特性,a ^ x = b。同理,ab的最高位(前n位)也有相同的性质。
先以最高位为例子,我们可以把所有的数字的最高位放到一个HashSet里面,然后使用1set里面的所有数字进行异或,如果得出的结果仍然在set里面,那么最终结果的最高位必然为1,否则为0。也即,先假定结果为1,然后与set中所有数字异或,假定a1异或得到结果ba ^ 1 = b),而b仍然在set里面,那么说明set中有两个数字异或能得到1a ^ b = 1)。否则,set中没有两个数字能够异或得到1,那么最终结果的最高位为1的假设失败,说明最终结果的最高位为0。以此类推可以得到第二位、第三位。。。的数字。
再做一下推广,我们将所有数字的前N位放到一个HashSet里面,然后使用之前N-1位得到的最大值前缀prefixset里面的所有数字进行异或,如果得出的结果仍然在set中,那么第N位必然为1,否则为0
举个例子,给定数组[14, 11, 7, 2],二进制表示分别为[1110, 1011, 0111, 0010]。题目说了,数字最长不会超过32位,所以应从i = 31开始,但是所有数字中最多位4位数,简单起见,我直接从最高位i=3开始

1
2
3
4
1. i = 3, set = {1000, 0000} => max = 1000
2. i = 2, set = {1100, 1000, 0100, 0000} => max = 1100
3. i = 1, set = {1110, 1010, 0110, 0010} => max = 1100
4. i = 0, set = {1110, 1011, 0111, 0010} => max = 1100

最终答案是1100 => 121011 ^ 0111 = 1100(11 ^ 7 = 12)

此解法时间复杂度为O(32n)=O(n),空间复杂度上,我们使用了一个HashSet用于存储所有数字,因此空间复杂度是O(n)。和暴力搜索解法相比,典型的空间换时间



public int findMaximumXOR(int[] nums) {
int max = 0;
int mask = 0;
for(int i = 31; i >= 0; i --){
// 为获取前n位的临时变量
mask = mask | (1 << i);
HashSet<Integer> set = new HashSet<>();
for(int num : nums){
// 将所有数字的前n位放入set中
set.add(mask & num);
}
// 假定第n位为1,前n-1位max为之前迭代求得
int tmp = max | (1 << i);
for(int pre : set){
// 查看`b`是否在
if(set.contains(tmp ^ pre)){
// b存在,第n位为1
max = tmp;
break;
}
}
}
return max;
}
https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/91049/Java-O(n)-solution-using-bit-manipulation-and-HashMap
When I tried to solve this problem, I quickly came up with the idea that we should build the answer bit by bit from the most significant bit. For any bit, it's easy to see if it can be built from all the numbers(just test whether there exist a 0 and 1 for that bit), but I had the difficulty to make sure that we're only using two numbers to do the build. The '''set.contains(tmp^prefix)''' solve my problem, we don't need to remember which two numbers we used to get the previous bit, we just need to test whether a new candidate(tmp), can be built, and make sure that its built by using two numbers

http://www.cnblogs.com/grandyang/p/5991530.html
按位遍历,题目中给定了数字的返回不会超过231,那么最多只能有32位,我们用一个从左往右的mask,用来提取数字的前缀,然后将其都存入set中,我们用一个变量t,用来验证当前位为1再或上之前结果res,看结果和set中的前缀异或之后在不在set中,这里用到了一个性质,若a^b=c,那么a=b^c,因为t是我们要验证的当前最大值,所以我们遍历set中的数时,和t异或后的结果仍在set中,说明两个前缀可以异或出t的值,所以我们更新res为t,继续遍历,如果上述讲解不容易理解,那么建议自己带个例子一步一步试试,并把每次循环中set中所有的数字都打印出来,基本应该就能理解了
http://blog.csdn.net/mebiuw/article/details/52901057
不过两个解法的核心都是: 
a^b = c ->a^c=b,b^c=a 
// a^b = c ->a^c=b,b^c=a public int findMaximumXOR(int[] nums) { int max = 0, mask = 0; for(int i = 31; i >= 0; i--){ //mask从0x01 ->0x03等一直变化,取后i位 mask = mask | (1 << i); Set<Integer> set = new HashSet<>(); for(int num : nums){ set.add(num & mask); } //tmp表示当前的最大 int tmp = max | (1 << i); System.out.println(mask+" "+tmp+" "+max); for(int prefix : set){ //证明存在一个数使得tmp^x = prefix if(set.contains(tmp ^ prefix)) { max = tmp; break; } } } return max; }
https://segmentfault.com/a/1190000007283296
利用XOR的性质,a^b = c, 则有 a^c = b,且 b^c = a;
所以每次从高位开始,到某一位为止,所能获得的最大的数。设置变量mask用来表示能形成的值,每一次将mask和其他的num相与得到的值加入set,表示在当前这一位上,数组里有这么多prefix。
假定在某一位上的任意两数xor能得到的最大值是tmp,那么他一定满足a(xor)b = tmp,其中set.contains(a) && set.contains(b). 所以,我们只需要判断b(xor)tmp的结果是不是在当前这一位下的set内,就可以知道这个tmp能不能又这个set中的任意两个数组成。
    public int findMaximumXOR(int[] nums) {
        int max = 0, mask = 0;
        // test each bit pose, 判断能不能构成所需要的值;
        for(int i = 31; i >= 0; i --) {
            // 每次都在之前的基础上更新mask
            mask = mask | (1 << i);
            Set<Integer> set = new HashSet<>();
            for(int num : nums) {
                // add the number which has the mask as its prefix;
                set.add(num & mask);
            }
            // 假设当前所能达到的最大值是这个temp值;
            int tmp = max | (1 << i);
            for(Integer prefix : set) {
                if(set.contains(prefix ^ tmp)) {
                    // 如果能组成就直接break 
                    max = tmp;
                    break;
                }
            }
        }
        return max;
    }
http://hankerzheng.com/blog/LeetCode-Maximum-XOR-of-Two-Numbers-in-an-Array
利用异或的特性, 这题可以有更好的解法. 对于异或运算, 我们有
如果a ^ b = c, 那么a ^ c = b.
根据这个定理, 我们从最高位开始找, 我们先将所有数的最高位存到一个Set中. 然后, 我们假设最终答案的最高位为1, 将数列中所有数的最高位和1进行异或运算, 异或得到的结果仍然在Set中, 那么说明最终答案的最高位一定为1, (由定理可得1 ^ x = b <==> b ^ x = 1, 对与数x, 一定存在一个数b, 使得x ^ b = 1), 否则最高位的答案一定为0.
假设我们已经知道最终答案的最高k位为prefix, 我们先将数列中所有数的最高k+1位存入Set中. 然后, 我们假设下一位的值为1, 将数列中所有数的最高k+1位与prefix*2 + 1进行异或运算, 如果异或得到的结果仍然在Set中, 那么说明最终答案的第k+1位一定为1, 否则, 最高位的答案一定为0.
因为x ^ (prefix*2+1) = b <==> x ^ b = prefix*2+1, 即对于数x, 一定存在一个数b, 使得他们异或的结果为prefix*2+1.
因此, 我们可以对最终答案的32位进行依次判断, 最终得到异或的最大值.
该算法的时间复杂度为O(32n) = O(n).

X.
解法II Set数据结构 + 位运算
https://discuss.leetcode.com/topic/63299/python-6-lines-bit-by-bit/2
Build the answer bit by bit from left to right. Let's say we already know the largest first seven bits we can create. How to find the largest first eight bits we can create? Well it's that maximal seven-bits prefix followed by 0 or 1. Append 0 and then try to create the 1 one (i.e., answer ^ 1) from two eight-bits prefixes from nums. If we can, then change that 0 to 1.
    int findMaximumXOR(vector<int>& nums) {
        int res = 0;
        unordered_set<int> pre;
        for (int i = 31; i >= 0; i--) {
            res <<= 1;
            pre.clear();
            for (auto n : nums) 
                pre.insert(n >> i);
            for (auto p : pre)
                if (pre.find((res ^ 1) ^ p) != pre.end()) {
                    res++;
                    break;
                }
        }
        return res;
    }
https://discuss.leetcode.com/topic/63213/java-o-n-solution-using-bit-manipulation-and-hashmap
example: Given [14, 11, 7, 2], which in binary are [1110, 1011, 0111, 0010].
Since the MSB is 3, I'll start from i = 3 to make it simplify.
  1. i = 3, set = {1000, 0000}, max = 1000
  2. i = 2, set = {1100, 1000, 0100, 0000}, max = 1100
  3. i = 1, set = {1110, 1010, 0110, 0010}, max = 1100
  4. i = 0, set = {1110, 1011, 0111, 0010}, max = 1100
The mask is 1000, 1100, 1110, 1111.
    public int findMaximumXOR(int[] nums) {
        int max = 0, mask = 0;
        for (int i = 31; i >= 0; i--) {
            mask |= (1 << i);
            HashSet<Integer> set = new HashSet<Integer>();
            for (int num : nums) {
                set.add(num & mask); // reserve Left bits and ignore Right bits
            }
            
            /* Use 0 to keep the bit, 1 to find XOR
             * 0 ^ 0 = 0 
             * 0 ^ 1 = 1
             * 1 ^ 0 = 1
             * 1 ^ 1 = 0
             */
            int tmp = max | (1 << i); // in each iteration, there are pair(s) whoes Left bits can XOR to max
            for (int prefix : set) {
                if (set.contains(tmp ^ prefix)) {
                    max = tmp;
                }
            }
        }
        return max;
    }
I saw deeply and found out a very important xor feature I missed, that is: if a^b=c, then a^c=b, b^c=a. That's why the answer using this code:
            for(int prefix : set){
                if(set.contains(tmp ^ prefix)) {
                    max = tmp;
                    break;
                }
            }
Agreed. Actually, this is the most important operation that made this code linear. Instead of choosing 2 from n, we verify if we can achieve maximum value -- 1 -- on each bit reversely based on the feature you have mentioned
to iteratively determine what would be each bit of the final result from left to right. And it narrows down the candidate group iteration by iteration. e.g. assume input are a,b,c,d,...z, 26 integers in total. In first iteration, if you found that a, d, e, h, u differs on the MSB(most significant bit), so you are sure your final result's MSB is set. Now in second iteration, you try to see if among a, d, e, h, u there are at least two numbers make the 2nd MSB differs, if yes, then definitely, the 2nd MSB will be set in the final result. And maybe at this point the candidate group shinks from a,d,e,h,u to a, e, h. Implicitly, every iteration, you are narrowing down the candidate group, but you don't need to track how the group is shrinking, you only cares about the final result.
    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){
                set.add(num & mask);
            }
            int tmp = max | (1 << i);
            for(int prefix : set){
                if(set.contains(tmp ^ prefix)) {
                    max = tmp;
                    break;
                }
            }
        }
        return max;
    }
X. 解法III 字典树(Trie) + 位运算
https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/130427/()-92
对于[3,10,5,25,2,8]测试用例,如果使用O(n^2)算法,选取3与剩下的数进行异或。这个过程中对于5,10,8的二进制前28位异或结果是一样的,对于10,8而言前30位异或结果是一致的。很容易想到对数组中的数的二进制,构建前缀树。相同前缀的计算结果可复用,就能提升效率。

建树

因为二进制元素只有0,1。考虑使用二叉树来构建前缀树。
  • 根节点为符号位0
  • 从高位到低位依次构建
  • 左子树为1节点,又子树为0节点
    [3,10,5,25,2,8] 按照以上规则构建如下图(省略高位0)
image
那么这颗树包含了数组中所有的数字,从根节点到叶子节点的一条路径表示数组中的一个十进制数的二进制编码。

找最大异或值

对于25 (0000 0000 0000 0000 0000 0000 0001 1001) 而言,从第31-6位(都是正数,不考虑符号位),都为0,,且数组中的数字31-6位都为0,因此最大异或结果31-6位只能为0。
当前使得异或结果最大的数x0000 0000 0000 0000 0000 0000 000? ????
当前指针curNode指向第图中根节点。
从第5位开始:
5 1 x的第5位为0,则结果最大。应选择右分支,curNode = curNode->right
4 1 x的第4位为0,则结果最大。应选择右分支,curNode = curNode->right
3 0 x的第3位为1,则结果最大。应选择左分支,curNode = curNode->left
2 0 x的第2位为1,则结果最大。应选择左分支,但树中左分支为null,只能选择右分支curNode = curNode->right 于是x的第2位为0。
1 1 x的第1位为0,则结果最大。应选择右分支,但树中右分支为null,只能选择左分支curNode = curNode->left 于是x的第1位为1。
找到的x为5(00101)
https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/91059/Java-O(n)-solution-using-Trie
    class Trie {
        Trie[] children;
        public Trie() {
            children = new Trie[2];
        }
    }
    
    public int findMaximumXOR(int[] nums) {
        if(nums == null || nums.length == 0) {
            return 0;
        }
        // Init Trie.
        Trie root = new Trie();
        for(int num: nums) {
            Trie curNode = root;
            for(int i = 31; i >= 0; i --) {
                int curBit = (num >>> i) & 1;
                if(curNode.children[curBit] == null) {
                    curNode.children[curBit] = new Trie();
                }
                curNode = curNode.children[curBit];
            }
        }
        int max = Integer.MIN_VALUE;
        for(int num: nums) {
            Trie curNode = root;
            int curSum = 0;
            for(int i = 31; i >= 0; i --) {
                int curBit = (num >>> i) & 1;
                if(curNode.children[curBit ^ 1] != null) {
                    curSum += (1 << i);
                    curNode = curNode.children[curBit ^ 1];
                }else {
                    curNode = curNode.children[curBit];
                }
            }
            max = Math.max(curSum, max);
        }
        return max;
    }
It gets me TLE as well. Ditching the Trie class and just using Object[] gets it accepted in about 185 ms:
    public int findMaximumXOR(int[] nums) {
        if(nums == null || nums.length == 0) {
            return 0;
        }
        // Init Trie.
        Object[] root = {null, null};
        for(int num: nums) {
            Object[] curNode = root;
            for(int i = 31; i >= 0; i --) {
                int curBit = (num >>> i) & 1;
                if(curNode[curBit] == null) {
                    curNode[curBit] = new Object[]{null, null};
                }
                curNode = (Object[]) curNode[curBit];
            }
        }
        int max = Integer.MIN_VALUE;
        for(int num: nums) {
            Object[] curNode = root;
            int curSum = 0;
            for(int i = 31; i >= 0; i --) {
                int curBit = (num >>> i) & 1;
                if(curNode[curBit ^ 1] != null) {
                    curSum += (1 << i);
                    curNode = (Object[]) curNode[curBit ^ 1];
                }else {
                    curNode = (Object[]) curNode[curBit];
                }
            }
            max = Math.max(curSum, max);
        }
        return max;
    }
https://discuss.leetcode.com/topic/64753/31ms-o-n-java-solution-using-trie
We add the number into the trie and find the max possible XOR result at the same time.
Node.set() method will set the new node in the trie if needed and return the new node.
After setting the node, find the opposite bit in the trie to maximize the possible XOR result.
    public class Node {
        Node one;
        Node zero;
        Node() {
            this.one = null;
            this.zero = null;
        }
        Node set(int n) {
            if (n == 0) {
                if (this.one == null) this.one = new Node();
                return this.one;
            }
            if (this.zero == null) this.zero = new Node();
            return this.zero;
        }
    }
    
    public int findMaximumXOR(int[] nums) {
        Node root = new Node();
        int re = 0;
        for (int num : nums) {
            int digit = num;
            int tmp = 0;
            Node setNode = root;
            Node findNode = root;
            int pos = 30;
            while (pos >= 0) {
                digit = (num >> pos) & 1;
                setNode = setNode.set(digit);
                if (digit == 1) {
                    if (findNode.one != null) {
                        tmp = tmp | (1 << pos);
                        findNode = findNode.one;
                    } else {
                        findNode = findNode.zero;
                    }
                } else {
                    if (findNode.zero != null) {
                        tmp = tmp | (1 << pos);
                        findNode = findNode.zero;
                    } else {
                        findNode = findNode.one;
                    }
                }
                pos--;
            }
            re = Math.max(re, tmp);
        }
        return re;
    }
https://www.geeksforgeeks.org/find-the-maximum-subarray-xor-in-a-given-array/
Insert + Query at same time

    
// Assumed int size
    static final int INT_SIZE = 32;
       
    // A Trie Node
    static class TrieNode
    {
        int value;  // Only used in leaf nodes
        TrieNode[] arr =  new TrieNode[2];
        public TrieNode() {
            value = 0;
            arr[0] = null;
            arr[1] = null;
        }
    }
    static TrieNode root;
      
    // Inserts pre_xor to trie with given root
    static void insert(int pre_xor)
    {
        TrieNode temp = root;
       
        // Start from the msb, insert all bits of
        // pre_xor into Trie
        for (int i=INT_SIZE-1; i>=0; i--)
        {
            // Find current bit in given prefix
            int val = (pre_xor & (1<<i)) >=1 ? 1 : 0;
       
            // Create a new node if needed
            if (temp.arr[val] == null)
                temp.arr[val] = new TrieNode();
       
            temp = temp.arr[val];
        }
       
        // Store value at leaf node
        temp.value = pre_xor;
    }
       
    // Finds the maximum XOR ending with last number in
    // prefix XOR 'pre_xor' and returns the XOR of this 
    // maximum with pre_xor which is maximum XOR ending 
    // with last element of pre_xor.
    static int query(int pre_xor)
    {
        TrieNode temp = root;
        for (int i=INT_SIZE-1; i>=0; i--)
        {
            // Find current bit in given prefix
            int val = (pre_xor & (1<<i)) >= 1 ? 1 : 0;
       
            // Traverse Trie, first look for a
            // prefix that has opposite bit
            if (temp.arr[1-val] != null)
                temp = temp.arr[1-val];
       
            // If there is no prefix with opposite
            // bit, then look for same bit.
            else if (temp.arr[val] != null)
                temp = temp.arr[val];
        }
        return pre_xor^(temp.value);
    }
       
    // Returns maximum XOR value of a subarray in 
        // arr[0..n-1]
    static int maxSubarrayXOR(int arr[], int n)
    {
        // Create a Trie and insert 0 into it
        root = new TrieNode();
        insert(0);
       
        // Initialize answer and xor of current prefix
        int result = Integer.MIN_VALUE;
        int pre_xor =0;
       
        // Traverse all input array element
        for (int i=0; i<n; i++)
        {
            // update current prefix xor and insert it 
                // into Trie
            pre_xor = pre_xor^arr[i];
            insert(pre_xor);
       
            // Query for current prefix xor in Trie and 
            // update result if required
            result = Math.max(result, query(pre_xor));
  
        }
        return result;
    }
X.
https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/184434/JavaTrie99
根据题目描述,我们需要找到最大的异或值,异或代表了两个数的二进制的不同程度,且越高位越不一样,异或值就越大,由于是按位比较,所以使用Trie树来当做基础数据结构。
我们可以总结出以下几点:
  1. 因为整型的位数是固定的,排除第一位符号位,Trie树的高度是常数的,即最高32层(包括root
  2. 由于只有01两个子节点,所以为了节省空间,可以使用二叉树的方式(或者数组和HashMap均可)
  3. 由于是异或,前缀位如果相同,异或值都是0,所以可以先找到第一个两个子节点都不为空的节点当做root
    class TrieNode {
        int val;
        TrieNode zero, one;
        boolean isEnd;
    }
    class TrieTree {
        TrieNode root;
        public TrieTree() {
            root = new TrieNode();
        }
        public void insert(int num) {
            TrieNode cur = root;
            int j = 1 << 30;
            for (int i = 0; i < 31; i++) {
                // 根据num在j位置的数目判断应该向0还是向1
                int b = (j & num) == 0 ? 0 : 1;
                if (b == 0 && cur.zero == null) {
                    cur.zero = new TrieNode();
                }
                if (b == 1 && cur.one == null) {
                    cur.one = new TrieNode();
                }
                cur = b == 0 ? cur.zero : cur.one;
                j >>= 1;
            }
            cur.isEnd = true;
            cur.val = num;
        }
    }

    public int findMaximumXOR(int[] nums) {
        if (nums == null || nums.length <= 1) {
            return 0;
        }
        TrieTree tree = new TrieTree();
        for (int n : nums) {
            tree.insert(n);
        }
        // 获取真正需要开始判断的root
        TrieNode cur = tree.root;
        while (cur.one == null || cur.zero == null) {
            cur = cur.zero != null ? cur.zero : cur.one;
        }
        return maxHelper(cur.one, cur.zero);

    }

    /**
     * 分治法,原则就是尽量使两个分支的高位不同
     * one是1分支,zero是0分支,可以看做图中的左区和右区
     * 1. 当1分支的下一位只有1时,找0分支的0,若没有,就找0分支的1
     * 2. 当1分支的下一位只有0时,找0分支的1,若没有,就找0分支的0
     * 3. 当1分支的下一位0,1均有时,看0分支:如果0分支只有1,则1分支走0,反之则走1
     * 4. 当0,1两个分支均有两个下一位时,尝试【1分支走1,0分支走0】和【1分支走0,0分支走1】两条路线并取最大值
     * */
    private int maxHelper(TrieNode one, TrieNode zero) {
        if (one.isEnd && zero.isEnd) {
            return one.val ^ zero.val;
        }
        if (one.zero == null) {
            return maxHelper(one.one, zero.zero == null ? zero.one : zero.zero);
        } else if (one.one == null) {
            return maxHelper(one.zero, zero.one == null ? zero.zero : zero.one);
        } else if (zero.zero == null) {
            return maxHelper(one.zero, zero.one);
        } else if (zero.one == null) {
            return maxHelper(one.one, zero.zero);
        } else {
            return Math.max(maxHelper(one.one, zero.zero), maxHelper(one.zero, zero.one));
        }
    }
X.
http://hankerzheng.com/blog/LeetCode-Maximum-XOR-of-Two-Numbers-in-an-Array
对输入的数组, 我们将其中的元素根据最高位的值分为两类, 最高位为0的一类, 最高位为1的一类, 如果两类都不为空, 那么该问题的最终结果一定是从第一类中找一个数和第二类中的一个数进行异或得到的结果.
那么此题就可以优化为, 从两个数列中各取一个数, 求两个数按位异或结果的最大值. 因此, 我们可以设计一个helper函数帮我们处理.
那么, 我们可以根据次高位再将数继续分成两类, 因此, 我们可以得到四个类, arr00arr01arr10arr11. 那么最终的解一定在(arr00arr11), (arr10arr01)中; 如果存在空集, 那么最终的解一定在(arr00arr01), (arr11arr10)中.
平均时间复杂度分析, 假设O(n)表示helper函数的时间复杂度, 其中n表示传入helper两个数列的总长度. 那么我们有 O(n) = n + 2 * O(n/2), 化简后可得 O(n) = O(n log n) + O(n) = O(n log n)

    def findMaximumXOR(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        def partition(nums, index):
            mask = 1 << index
            arr0, arr1 = [], []
            for num in nums:
                if num & mask:
                    arr1.append(num)
                else:
                    arr0.append(num)
            return arr0, arr1

        def helper(arr0, arr1, index):
            if not arr0 and not arr1:
                return 0
            if not arr0 or not arr1:
                if index == 0:
                    return 0
                arr = arr0 if arr0 else arr1
                arr0, arr1 = partition(arr, index - 1)
                return helper(arr0, arr1, index - 1)
            if index == 0:
                return arr0[0] ^ arr1[0]

            arr00, arr01 = partition(arr0, index - 1)
            arr10, arr11 = partition(arr1, index - 1)
            if arr00 and arr11 or arr10 and arr01:
                return max(helper(arr00, arr11, index - 1), helper(arr01, arr10, index - 1))
            else:
                return max(helper(arr01, arr11, index - 1), helper(arr00, arr10, index - 1))

        return helper(nums, [], 32)
X.  解法I 分治法(Divide and Conquer)+ 位运算(Bit Manipulation)
https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/91124/Java-bit-manipulation-and-divide-into-two-groups.
    public int findMaximumXOR(int[] nums) {
       int max = Integer.MIN_VALUE;
       int highBit = 0, count = 0;
       for(int num: nums) max = Math.max(max, num);
       while(count<=31){
           if((max & (1<<count)) != 0) highBit = count;
           count++;
       }
       List<Integer> isOne = new ArrayList<>();
       List<Integer> notOne = new ArrayList<>();
       
       for(int num: nums){
           if(((num>>highBit) & 1) == 1) isOne.add(num);
           else notOne.add(num);
       }
       
       return recur(isOne, notOne, highBit-1);
    }
    private int recur(List<Integer> isOne, List<Integer> notOne, int highBit){
        if(isOne.size()==1 && notOne.size()==1) return isOne.get(0) ^ notOne.get(0);
        
        List<Integer> l11 = new ArrayList<>();
        List<Integer> l10 = new ArrayList<>();
        List<Integer> l01 = new ArrayList<>();
        List<Integer> l00 = new ArrayList<>();
        
        for(int num: isOne){
            if(((num>>highBit) & 1) != 0) l11.add(num);
            else l10.add(num);
        }
        
        for(int num: notOne){
            if(((num>>highBit) & 1) != 0) l01.add(num);
            else l00.add(num);
        }
        
        int max = 0;
        if(l11.size()!=0 && l00.size()!=0) max = recur(l11, l00, highBit-1);
        if(l10.size()!=0 && l01.size()!=0) max = Math.max(max, recur(l10,l01,highBit-1));
        return max;
    }

Time: O(nlog(HighestBit))
http://bookshadow.com/weblog/2016/10/15/leetcode-maximum-xor-of-two-numbers-in-an-array/
求数组中两两数字的异或运算的最大值,朴素的解法是O(n ^ 2),不满足题目要求。
异或运算(XOR)的实质是“同零异一”。
题目描述中a[i]的范围[0, 2^32),至多有32位,因此可以通过位运算处理。
递归函数定义为:solve(nums0, nums1, mask)。

令mask从最高位(1<<31)向最低位(1)逐步右移,将数组nums按照如下规则分成两部分:

nums中,与mask按位与结果为0的所有数字,记为nums0

nums中,与mask按位与结果为1的所有数字,记为nums1

如果nums0与nums1其一为空,说明nums中所有数字在该二进制位同0或同1,其异或结果一定为0

如果nums0与nums1均不为空,说明nums中的数字在该位的异或值为1,令mask右移一位,再将nums0与nums1进一步划分成四部分:

nums0中,与mask按位与结果为0的所有数字,记为nums00

nums0中,与mask按位与结果为1的所有数字,记为nums01

nums1中,与mask按位与结果为0的所有数字,记为nums10

nums1中,与mask按位与结果为1的所有数字,记为nums11

分支1:如果nums00与nums11同时存在,递归求解solve(nums00, nums11, mask)的结果

分支2:如果nums01与nums10同时存在,递归求解solve(nums01, nums10, mask)的结果

从上述两个分支中求最大值ans

若ans为0,说明上述两分支均不存在,nums中mask对应二进制位的所有数字同0或者同1,此时计算分支3

分支3:递归计算solve(nums0, nums1, mask) - mask

最后将结果+mask * 2,并返回
def findMaximumXOR(self, nums): """ :type nums: List[int] :rtype: int """ def solve(nums0, nums1, mask): if mask <= 1: return mask mask /= 2 nums01 = [n for n in nums0 if n & mask] nums00 = [n for n in nums0 if not n & mask] nums11 = [n for n in nums1 if n & mask] nums10 = [n for n in nums1 if not n & mask] ans = 0 if nums10 and nums01: ans = max(ans, solve(nums10, nums01, mask)) if nums00 and nums11: ans = max(ans, solve(nums00, nums11, mask)) if not ans: ans = solve(nums0, nums1, mask) - mask return ans + mask * 2 mask = 1 << 31 while mask: nums0 = [n for n in nums if not n & mask] nums1 = [n for n in nums if n & mask] if nums0 and nums1: break mask >>= 1 return solve(nums0, nums1, mask)

public int findMaximumXOR(int[] nums) {
int result = 0;
for(int i = 0; i < nums.length; i++){
for (int j = i + 1; j < nums.length; j ++){
result = Math.max(nums[i] ^ nums[j]);
}
}
return result;
}

TODO
https://discuss.leetcode.com/topic/64431/a-solution-based-on-bartoszkp-s-with-missing-test-cases
https://discuss.leetcode.com/topic/63759/c-o-nlogk-solution-with-ordering-bits-with-o-logk-additional-memory-for-recursion-stack

http://www.1point3acres.com/bbs/thread-202685-3-1.html
Related:
https://www.geeksforgeeks.org/minimum-xor-value-pair/
Given an array of integers. Find the pair in an array which has minimum XOR value.
Examples :
Input : arr[] =  {9, 5, 3}
Output : 6
        All pair with xor value (9 ^ 5) => 12, 
        (5 ^ 3) => 6, (9 ^ 3) => 10.
        Minimum XOR value is 6

    static int minXOR(int arr[], int n)
    {
        // Sort given array
        Arrays.parallelSort(arr);
  
        int minXor = Integer.MAX_VALUE;
        int val = 0;
  
        // calculate min xor of consecutive pairs
        for (int i = 0; i < n - 1; i++) {
            val = arr[i] ^ arr[i + 1];
            minXor = Math.min(minXor, val);
        }
  
        return minXor;
    }

A further more Efficient solution can solve the above problem in O(n) time under the assumption that integers take fixed number of bits to store. The idea is to use Trie Data Structure. Below is algorithm.
1). Create an empty trie. Every node of trie contains two children
    for 0 and 1 bits.
2). Initialize min_xor = INT_MAX, insert arr[0] into trie
3). Traversal all array element one-by-one starting from second.
     a. First find minimum setbet difference value in trie 
        do xor of current element with minimum setbit diff that value 
     b. update min_xor value if required
     c. insert current array element in trie 
4). return min_xor
    static final int INT_SIZE = 32;
  
    // A Trie Node
    static class TrieNode {
        int value; // used in leaf node
        TrieNode[] Child = new TrieNode[2];
  
        public TrieNode()
        {
            value = 0;
            Child[0] = null;
            Child[1] = null;
        }
    }
    static TrieNode root;
  
    // utility function insert new key in trie
    static void insert(int key)
    {
        TrieNode temp = root;
  
        // start from the most significant bit, insert all
        // bit of key one-by-one into trie
        for (int i = INT_SIZE - 1; i >= 0; i--) {
            // Find current bit in given prefix
            int current_bit = (key & (1 << i)) >= 1 ? 1 : 0;
  
            // Add a new Node into trie
            if (temp != null && temp.Child[current_bit] == null)
                temp.Child[current_bit] = new TrieNode();
  
            temp = temp.Child[current_bit];
        }
  
        // store value at leafNode
        temp.value = key;
    }
  
    // Returns minimum XOR value of an integer inserted
    // in Trie and given key.
    static int minXORUtil(int key)
    {
        TrieNode temp = root;
  
        for (int i = INT_SIZE - 1; i >= 0; i--) {
            // Find current bit in given prefix
            int current_bit = (key & (1 << i)) >= 1 ? 1 : 0;
  
            // Traversal Trie, look for prefix that has
            // same bit
            if (temp.Child[current_bit] != null)
                temp = temp.Child[current_bit];
  
            // if there is no same bit.then looking for
            // opposite bit
            else if (temp.Child[1 - current_bit] != null)
                temp = temp.Child[1 - current_bit];
        }
  
        // return xor value of minimum bit difference value
        // so we get minimum xor value
        return key ^ temp.value;
    }
  
    // Returns minimum xor value of pair in arr[0..n-1]
    static int minXOR(int arr[], int n)
    {
        int min_xor = Integer.MAX_VALUE; // Initialize result
  
        // create a True and insert first element in it
        root = new TrieNode();
        insert(arr[0]);
  
        // Traverse all array element and find minimum xor
        // for every element
        for (int i = 1; i < n; i++) {
            // Find minimum XOR value of current element with
            // previous elements inserted in Trie
            min_xor = Math.min(min_xor, minXORUtil(arr[i]));
  
            // insert current array value into Trie
            insert(arr[i]);
        }
        return min_xor;
    }

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts