leetcode 421.数组中两个数的最大异或值 - 字典树 + 贪心

leetcode 421.数组中两个数的最大异或值 - 字典树 + 贪心

题干

给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。
进阶:你可以在 O(n) 的时间解决这个问题吗?

示例 1:
输入:nums = [3,10,5,25,2,8]
输出:28
解释:最大运算结果是 5 XOR 25 = 28.

示例 2:
输入:nums = [0]
输出:0

示例 3:
输入:nums = [2,4]
输出:6

示例 4:
输入:nums = [8,10,2]
输出:10

示例 5:
输入:nums = [14,70,53,83,49,91,36,80,92,51,66,70]
输出:127

提示:
1 <= nums.length <= 2 * 104
0 <= nums[i] <= 231 - 1

知识点&算法

因为异或的时候需要逐位比较,而数字的二进制表示可能长短不一,所以将他们统一储存为31位二进制数(因为数据范围是0 - 2 ^ 31 - 1)。
然后枚举数,和其他的数进行异或运算,取最大异或值。
用字典树对已有的数据进行维护。
对于当前枚举的数,从高位开始,贪心地尽可能选取相反的位值,如果字典树中已有的数无法满足取相反位,就取相同位。

题解

class Solution {
public:
    struct trie{
        struct trie* nextDigit[2];
        trie(){
            memset(nextDigit,0,sizeof(nextDigit));
        }
    };
    int shiftLimit = 30;
    trie* root = new trie;
    trie* node;

    void insert(int num){
        node = root;
        for(int i = shiftLimit ; i >= 0 ; --i){
            int digit = (num >> i) & 1;
            if(node->nextDigit[digit] == nullptr) node->nextDigit[digit] = new trie;
            node = node->nextDigit[digit];
        }
        return;
    }

    int search(int num){
        node = root;
        int res = 0;
        for(int i = shiftLimit ; i >= 0 ; --i){
            int digit = (num >> i) & 1;
            if(digit == 0){
                if(node->nextDigit[1]){
                    res = res * 2 + 1;
                    node = node->nextDigit[1];
                }else{
                    res *= 2;
                    node = node->nextDigit[0];
                }
            }else{
                if(node->nextDigit[0]){
                    res = res * 2 + 1;
                    node = node->nextDigit[0];
                }else{
                    res *= 2;
                    node = node->nextDigit[1];
                }
            }
        }
        return res;
    }

    int findMaximumXOR(vector<int>& nums) {
        int n = nums.size(),ans = 0;
        for(int i = 0 ; i < n ; ++i) insert(nums[i]);
        for(int i = 0 ; i < n ; ++i) ans = max(ans,search(nums[i]));
        return ans;
    }
};

最后主函数更新答案的部分可以只写一个循环,只匹配枚举的数和此前的数,枚举的数和此后的数相当于枚举到此后的数与之前的数,所以不会遗漏。
i = 1 ; i < n 相比 i = 0 ; i < n - 1可以节省每次循环一个运算的时间开销

for(int i = 1 ; i < n ; ++i){
	insert(nums[i-1]);
	ans = max(ans,search(nums[i]));
}
上一篇:P2444 [POI2000]病毒


下一篇:Acwing 143. 最大异或对(Trie树)