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

题目大意

给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 。

找到 ai 和aj 最大的异或 (XOR) 运算结果,其中0 ≤ i, j < n 。

你能在O(n)的时间解决这个问题吗?

示例:

输入: [3, 10, 5, 25, 2, 8]

输出: 28

解释: 最大的结果是 5 ^ 25 = 28.

解题思路

构建一个字典树(深度是31,即二进制数字的最高位数),首先遍历数组,生成该字典树。生成方式:对于当前数字num,从高位向低位计算,如果(num>>i)&1=true,表示该位置是1,生成右子树,反之生成左子树。
注:遍历数组,时间为O(N),每个数字生成字典树是常量时间(一共就31位),因此生成字典树的时间复杂度为O(N)。

字典树生成后,遍历数组;对于当前数字num,仍然从高位向低位计算(因为我们想让异或结果最大, 因此应该从高位开始比较)。

  • (num>>i)&1=true表示num在位置i位置是1,为了让异或结果最大,我们应该在字典树中找一下,当前root中是否存在左子树(表示某个数字在该位置上为0),如果存在左子树,则将当前结果+(1<<i)(表示i位置异或之后是1),且root=root->left。如果不存在左子树,则直接root=root->right。
  • (num>>i)&1=false,与上述操作相反即可。

注:字典树是根据整个数组生成的,因此root在字典树中移动的时候,对应的是数组中的某个数字,符合题意;

// 字典树
struct TrieRoot
{
	TrieRoot * left;
	TrieRoot * right;
};

class Solution {
private:
	// 将某个数字插入字典树中
	void insert(TrieRoot* root, int num){
		for (int i = 30; i >= 0; --i){
			if ((num >> i) & 1){
				if (!root->right)
					root->right = new TrieRoot;
				root = root->right;
			}
			else{
				if (!root->left)
					root->left = new TrieRoot;
				root = root->left;
			}
		}
	}
	
public:
    int findMaximumXOR(vector<int>& nums) {
    	TrieRoot * root = new TrieRoot;
		// 将数组元素插入字典树
    	for (int i = 0; i < nums.size(); ++i){
    		insert(root, nums[i]);
    	}

    	int res = 0, tmp = 0;
    	TrieRoot * curNode = root;
		
		// 计算每个数字在数组中的最大异或值
    	for (int i = 0; i < nums.size() ++i){
    		tmp = 0;
    		curNode = root;
			// 从高位向低位计算,保证异或结果最大
    		for (int j = 30; j >= 0 --j){
    			if ((nums[i] >> j) & 1){
    				if (curNode->left){
    					tmp += (1 << j);
    					curNode = curNode->left;
    				}
    				else
    					curNode = curNode->right;
    			}
    			else{
    				if (curNode->right){
    					tmp += (1 << j);
    					curNode = curNode->right;
    				}
    				else
    					curNode = curNode->left;
    			}
    		}
    		res = max(res, tmp);
    	}
    	return res;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_41092190/article/details/106419558