给定一个非空数组,数组中元素为 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.
思路分析:这种题就不要暴力法,指名道姓说要O(n)。根据提示需要使用建树。
首先我们需要知道,二进制高位为1会大于低位的所有和,比如"11111111"最高位代表的"1"按权展开为128,而后面的“1111111”按权展开的和也只是127。所以进行异或时应该尽量选择高位异或结果为“1”的。
第一步:遍历数组,我们按照二进制[31,30,…,1, 0]各位的状态进行建树,left放置0,right放置1。比如某个int型数的二进制是"0110110…",我们需要将其放置到[left,right,right,left,right,right,left…]。
第二步:遍历数组,按照贪心策略,尽量维持当前选择的方向能保证当前能位异或结果为1。
class Solution {
public:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
int findMaximumXOR(vector<int>& nums) {
TreeNode *root = new TreeNode(-1);
int maxRes = 0;
//建立二叉树
for (auto num : nums){
TreeNode *treePtr = root;
//按照[31,30, .... 1,0]二进制数字串的位位0、1进行区别,1放左边,0放右边
//从高位向低位逐渐检测,递归寻找放的位置
for (int i = 31; i >= 0; --i){
if ((num & (1 << i)) == 0){
//num的第i位为0,需要放到当前节点的left
if (treePtr->left == NULL){
treePtr->left = new TreeNode(0);
}
treePtr = treePtr->left;//转移到left,进行i + 1的放置
}
else{
//num的第i位为1,需要放到当前节点的right
if (treePtr->right == NULL){
treePtr->right = new TreeNode(1);
}
treePtr = treePtr->right;//转移到right,进行i + 1的放置
}
}
//最后放在底部节点的left位置
treePtr->left = new TreeNode(num);
}
//搜索
for (auto num : nums){
TreeNode *treePtr = root;
//从高位向低位寻找,贪心策略,尽量保持当前位异或为1
for (int i = 31; i >= 0; --i){
if ((num & (1 << i)) == 0){
//如果num的第i位0,则应该优先选择right,因为right为1,这样1 ^ 0 == 1
if (treePtr->right != NULL){
treePtr = treePtr->right;
}
else{
treePtr = treePtr->left;
}
}
else{
//如果此位是1,则应该优先选择left
if (treePtr->left != NULL){
treePtr = treePtr->left;
}
else{
treePtr = treePtr->right;
}
}
}
//最后去底端的left进行异或操作
maxRes = max(maxRes, treePtr->left->val ^ num);
}
return maxRes;
}
};