1707. 与数组中元素的最大异或值

Posted Mrsdwang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1707. 与数组中元素的最大异或值相关的知识,希望对你有一定的参考价值。

思路:
这个题可以在421. 数组中两个数的最大异或值建立的字典树基础上完成。
这道题多了两个要求,第一个是nums里小于queries[i][1]得元素与queries[i][0]的元素相异或并取最大值,返回的结果数组中结果的存放顺序要按照queries的顺序。
那么我们考虑第一个要求,如果我们每次都遍历nums来找出小于queries[i][1]的元素的话时间复杂度就会增加可以推测时间复杂度为O(nm),那么我们就考虑对nums排序了,那么递增的比较只需要遍历一遍。那queries要不要处理呢?因为我们已经对nums排序了,如果遇到的queries[i][1]大于queries[i+1][1]的情况,那么我们指向nums的指针,当前指向的元素就可能大于queries[i+1][1],而queries[i+1][1]可能小于全部的nums元素,为了只遍历一遍,但这里可以多加一个判断如果queries[i+1][1]<nums[0]就加入-1,但运行的时候还是出错了,不知道啥情况。为了处理更为方便,我们还是对queries按照queries[i][1]的大小进行排序。这样queries就能按顺序的让nums元素递增的加入字典树,遍历的次数减少了。
但答案数组需要我们按照queries原来的顺序返回答案,那么我们该如何做呢,我们可以在每个queries元素加上当前的位置信息,最后加入结果数组的时候就按照这个信息赋值即可。

代码:

class Solution {
private:
//我们不需要给树的节点赋值,只要创建就说明有1或者0
struct trie{
    trie* left = nullptr;  //左用来代表0
    trie* right = nullptr; //右用来代表1
    trie(){}
};

trie* root = new trie();
static const int HIGH_BIT = 30;

public:
    void addtotrie(int num){  //用来将十进制数转为二进制数存入树里
        trie* cur = root;
        for(int i = HIGH_BIT;i>=0;--i){
            int bit = (num >> i)&1; //从高位不断获取HIGH_BIT-i位的二进制数
            if(bit == 0){     //如果这个位二进制数为0
                if(!cur->left){  //没0就创建0,并移动到0处
                    cur->left = new trie();
                }
                cur = cur->left;
            }
            else if(bit == 1){ //同上
                if(!cur->right){
                    cur->right = new trie();
                }
                 cur = cur->right;
            }
        }
    }

    int retxornum(int num){  //用来找到num异或的最大值
        trie* cur = root;
        int nowxor=0;
        for(int i= HIGH_BIT;i>=0;--i){
            int bit = (num >> i)&1;
            if(bit == 0){
                if(cur->right){   //遇0找1
                    cur = cur->right;
                    nowxor = nowxor*2+1;
                }
                else {  //没有1就找0
                    cur = cur ->left;
                    nowxor = nowxor*2;
                }
            }
            else if(bit == 1){  //遇1找0
                if(cur->left){
                    cur = cur->left;
                    nowxor = nowxor*2+1;
                }
                else{ //没有0就找1
                    cur = cur->right;
                    nowxor = nowxor*2;
                }
            }
        }
        return nowxor; //返回得到的异或值
    }

    vector<int> maximizeXor(vector<int> &nums, vector<vector<int>> &queries) {
    sort(nums.begin(),nums.end());
    int lenq = queries.size();
    for(int i=0;i<lenq;++i){  //加上位置信息 queries[i][2]=i
        queries[i].push_back(i);
    }
    sort(queries.begin(),queries.end(),[](auto &x,auto &y){return x[1]<y[1];}); //按照queries[i][1]进行排序
    vector<int> res(lenq);
    trie *cur = root;
    int idx=0,n=nums.size();
    for(auto& q: queries){
        int x=q[0],y=q[1],qid=q[2];
        while(idx<n && nums[idx]<=y){ //只要满足nums小于queries[i][1]就加入进字典树里
            addtotrie(nums[idx]);
            ++idx;
        }
        if(idx==0) res[qid] = -1;
        else res[qid] = retxornum(x); //加入满足queries的nums数后就进行求最大的异或值
    }
    return res;
    }
};

上述是离线+字典树,另外一种称为 在线+字典树。
在线比离线多一个信息,就是每个节点存放了以他的根节点的子树的最小的数min,这些数都是nums里面的,所以我们传入参数求最大异或时,再加一个条件就是当前的数小于该节点存放min的值我们就能走,这样就比离线的方法更快,因为不用排序了,在加数进树里的同时就明确了哪些节点的数会满足条件,所以性能会更优。
但不知道为啥用上面的代码直接改会有莫名的问题,找不出就直接看官方题解的学习了,顺便学了更为简洁的写法
代码:

class trie{
public:
    const int HIGH_BIT = 30;
    trie* children[2] = {}; //这里优化的特别好,通过数组的0,1位置代表了0和1
    int min =INT_MAX;

    void addtotrie(int num){
        trie* cur = this;
        cur->min = std::min(num,cur->min);
        for(int i=HIGH_BIT-1;i>=0;i--){
            int bit = (num>>i)&1;  //如果是bit=0就用children[0]的位置,bit=1,就用[1]
            if(!cur->children[bit]){  
                cur->children[bit] = new trie();
            }
            cur = cur->children[bit];
            cur->min = std::min(cur->min,num); //对该节点存放以它为根节点的子树的最小值
        }
    }

    int retmaxxor(int num,int limit){
        trie* cur =this;
        if(cur->min>limit) return -1;
        int res = 0;
        for(int i = HIGH_BIT-1;i>=0;--i){
            int bit = (num >> i)&1;
            //bit ^ 1是因为如果bit=0,我们应该找1,所以0 ^ 1=1.如果bit=1我们应该找0,1 ^ 0 =1
            if(cur->children[bit^1]!=nullptr&&cur->children[bit^1]->min<=limit){ //两个条件,新条件就是如果该支路min值小于limit那么就满足条件能走下去
                res |= 1<<i;  //这里注意优先级就能理解了。1先右移i位在和res相异或,任何数与1或都能将该位置1。要注意这里是求的异或的结果,而不与num异或的值,所以最理想的情况就是异或结果全为1,所以直接用1左移相应的位即可,如果没有理想情况,则左移1不会填充所有的位,其中会有0.
                bit ^= 1;
            }
            cur=cur->children[bit];
        }
        return res;
    }
};

class Solution {
public:
    vector<int> maximizeXor(vector<int>& nums, vector<vector<int>>& queries) {
        trie* t =new trie();
        int lenq = queries.size();
        for(int num : nums){
            t->addtotrie(num);
        }
        vector<int> res(lenq);
        for(int i=0;i<lenq;++i){
            res[i] = t->retmaxxor(queries[i][0],queries[i][1]);
        }
        return res;
    }
};

以上是关于1707. 与数组中元素的最大异或值的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode每日一题——1707. 与数组中元素的最大异或值(字典树)

LeetCode 1707 与数组中元素的最大异或值[字典树 异或] HERODING的LeetCode之路

与数组中元素的最大异或值(字典树)(小于等于某元素的最大异或值)

2021-08-07:与数组中元素的最大异或值。给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries ,其中 queries[i] = [xi, mi] 。第 i 个查询的答案是

LeetCode﹝前缀树ி﹞最长单词键值映射最大异或值

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