力扣1707题:字典树的应用

本文介绍如何使用字典树(Trie)数据结构解决给定数组中与查询元素异或的最大值问题。通过排序数组和查询、构建字典树并实现插入和查询操作,快速找到满足条件的最大异或值。适合解决与数组元素异或操作的高效查询问题。
摘要由CSDN通过智能技术生成

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

给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries ,其中 queries[i] = [xi, mi] 。
第 i 个查询的答案是 xi 和任何 nums 数组中不超过 mi 的元素按位异或(XOR)得到的最大值。换句话说,答案是 max(nums[j] XOR xi) ,其中所有 j 均满足 nums[j] <= mi 。如果 nums 中的所有元素都大于 mi,最终答案就是 -1 。
返回一个整数数组 answer 作为查询的答案,其中 answer.length == queries.length 且 answer[i] 是第 i 个查询的答案。

示例 1:
输入:nums = [0,1,2,3,4], queries = [[3,1],[1,3],[5,6]]
输出:[3,3,7]
解释:

  1. 0 和 1 是仅有的两个不超过 1 的整数。0 XOR 3 = 3 而 1 XOR 3 = 2 。二者中的更大值是 3 。
  2. 1 XOR 2 = 3.
  3. 5 XOR 2 = 7.

示例 2:
输入:nums = [5,2,4,6,6,3], queries = [[12,4],[8,1],[6,3]]
输出:[15,-1,5]

插入
将nums中的每个数字转换为长度为L的二进制数,根据字典树的插入算法,将该数字插入到字典树中,每个结点都有两个孩子children[2],根据二进制数的0和1,判断是否有孩子。
查询异或最大值
设比较值为25,即11001,从头开始遍历字典树,判断每个结点存在于children的位置,如果在0位置则表示该结点表示二进制中的一位:0,如果在1位置则表示二进制中的一位:1,遍历该字典树,遍历过程中,为寻找最大异或数,每次都要判断与比较值中对应位异或值为1的结点是否存在,由于该树是从二进制高位开始往低位延伸,所以越早遇到与比较值中对应位异或值为1的结点,得出的结果会越大,所以只需要从头开始遍历,遇到这种结点,就可以在结果中将该位记为1,遍历完成,得到最大值。
在这里插入图片描述


class Trie {
public:
    const int L = 30;

    Trie* children[2] = {};

    void insert(int val) {
        Trie* node = this;
        for (int i = L - 1; i >= 0; --i) {
            int bit = (val >> i) & 1;   //从高位开始插入
            if (node->children[bit] == nullptr)
            {
                node->children[bit] = new Trie();
            }
            node = node->children[bit];
        }
    }

    int getMaxXor(int val) {
        int ans = 0;
        Trie* node = this;
        for (int i = L - 1; i >= 0; --i) {
            int bit = (val >> i) & 1;
            if (node->children[bit ^ 1] != nullptr) {
                ans |= 1 << i;                      //保存结果
                bit ^= 1;                           //从与自身异或值为1的结点位置继续往下查询
            }
            node = node->children[bit];
        }
        return ans;
    }
};

class Solution1707 {
public:
    vector<int> maximizeXor(vector<int> &nums, vector<vector<int>> &queries) {
        sort(nums.begin(), nums.end());
        int numQ = queries.size();
        for (int i = 0; i < numQ; ++i) 
        {
            queries[i].push_back(i);
        }
        // {{12,4,0},{8,1,1},{6,3,2}}

        //按照  每个元素中的第二个数的大小排序
        sort(queries.begin(), queries.end(), [](auto &x, auto &y) { return x[1] < y[1]; });
        //===》  {8,1,1} {6,3,2}  {12,4,0}  这样可以保持对应关系 例如寻找与1的异或最大值时,字典树 中的元素都是小于等于1的。
        vector<int> ans(numQ);
        Trie* t = new Trie();
        int idx = 0, n = nums.size();
        for (auto &q : queries)
        {
            //x  8   m 1  qid 1
            int x = q[0], m = q[1], qid = q[2];
            while (idx < n && nums[idx] <= m) {
                t->insert(nums[idx]);
                ++idx;
            }
            if (idx == 0) { // 字典树为空
                ans[qid] = -1;
            } else {
                ans[qid] = t->getMaxXor(x);
            }
        }
        return ans;
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Michael.Scofield

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值