二进制Trie树 + XOR LeetCode 421、1707、1803

2 篇文章 0 订阅

二进制Trie树

一个整数类型可以看做一个固定长度的二进制串,因此可以使用Trie树以二进制的形式保存整型元素。这种数据结构可以与位运算相互配合。

LeetCode421. Maximum XOR of Two Numbers in an Array

Given an integer array nums, return the maximum result of nums[i] XOR nums[j], where 0 ≤ i ≤ j < n.
1 <= nums.length <= 2 * 104
0 <= nums[i] <= 231 - 1

题解

将每个数组元素看做一个31位的二进制串,插入Trie树中。
对于一个数,求其与Trie树中的元素的异或值的最大值。
则其从30位遍历到0位,总是优先取每一位的相反值,则走到Trie树的叶子节点时,得到的就是与其异或值最大的数组元素。

code

class Solution {
public:
    
    int findMaximumXOR(vector<int>& nums) {
        int pow2[31];
        pow2[0] = 1;
        for (int i = 1; i <= 30; i++) 
            pow2[i] = 2*pow2[i-1];
        int sons[32*nums.size()+10][2];
        memset(sons, 0, sizeof(sons));
        int size = 1, maxn = 0, cur, total;
        for (int num:nums)
        {
            int cur = 0;
            for (int i = 30; i >= 0; i--)
            {
                int bit = (pow2[i]&num) > 0;
                if (!sons[cur][bit])
                {
                    sons[cur][bit] = size++;
                }
                cur = sons[cur][bit];
            }
        }
        for (int num:nums)
        {
            cur = 0, total = 0;
            for (int i = 30; i >= 0; i--)
            {
                int bit = (pow2[i]&num) > 0;
                if (sons[cur][1-bit])
                {
                    total += pow2[i];
                    cur = sons[cur][1-bit];
                }
                else
                {
                    cur = sons[cur][bit];
                }
            }
            maxn = max(maxn, total);
        }
        return maxn;
    }
};

LeetCode1707. Maximum XOR With an Element From Array

nums是一个包含非负整数的数组。queries是一个查询数组。给定nums和queries要求给出查询结果。
每个查询queries[i]=[xi,mi],询问nums中不大于mi的元素与xi的异或值的最大值。如果所有元素都大于mi,返回-1。

题解

对nums进行排序;对queries根据mi进行排序,并且保存原来的位置信息。
则按序将nums中的元素插入Trie树,同时按序执行查询query,并且保证所有不大于query.m的元素都在Trie树中时,大于query.m的元素则还未插入树中。
那么每次查询的求法就与上题相同。

代码

// 对queries根据mi排序,对nums排序。
// 依次将nums插入trie树,同时查看query, 当trie树中的元素都小于query.mi而数组剩余元素大于query.mi时,从trie树中找目标值。
bool cmp(const vector<int>& lhs, const vector<int>& rhs)
{
    return lhs[1]<rhs[1];
}

class MyTrie { 
private:
    int pow2[31];
    int sons[32 * 100000 + 10][2];
    int size;

public:
    MyTrie()
    {
        pow2[0] = 1;
        for (int i = 1; i < 31; i++) 
            pow2[i] = 2*pow2[i-1];
        memset(sons, 0, sizeof(sons));
        size = 1;
    }
    void insert(int num)
    {
        int cur = 0;
        for (int i = 30; i >= 0; i--)
        {
            int bit = (num & pow2[i]) >0;
            if (!sons[cur][bit])
                sons[cur][bit] = size++;
            cur = sons[cur][bit];
        }
    }
    int maxXor(int x)
    {
        if (size == 1) return -1;
        int cur = 0, total = 0;
        for (int i = 30; i >= 0; i--)
        {
            int bit = (x & pow2[i])>0;
            if (sons[cur][1-bit])
            {
                cur = sons[cur][1-bit];
                total += pow2[i];
            }
            else 
            {
                cur = sons[cur][bit];
            }
        }
        return total;
    }
};
    


class Solution {
public:
    vector<int> maximizeXor(vector<int>& nums, vector<vector<int>>& queries) {
        sort(nums.begin(), nums.end());
        for (int i = 0; i < queries.size(); i++) queries[i].push_back(i);
        sort(queries.begin(), queries.end(), cmp);
        vector<int> res(queries.size());
        MyTrie mytrie;
        int idx = 0;
        for (int i = 0; i < queries.size(); i++)
        {
            while (idx < nums.size() && nums[idx] <= queries[i][1])
                mytrie.insert(nums[idx++]);
            res[queries[i][2]] = mytrie.maxXor(queries[i][0]);
        }
        return res;
    }
};

1803. Count Pairs With XOR in a Range

Given a (0-indexed) integer array nums and two integers low and high, return the number of nice pairs.
A nice pair is a pair (i, j) where 0 <= i < j < nums.length and low <= (nums[i] XOR nums[j]) <= high.

  • 1 <= nums.length <= 2 * 104
  • 1 <= nums[i] <= 2 * 104
  • 1 <= low <= high <= 2 * 104

题解

一个技巧:Cnt([low, high]) == Cnt((-, high+1)) - Cnt((-, low))。
因此可以将问题转化为给定上限upper和一个数x,计算数组nums中与x的异或值小于upper的元素的数量。
建立Trie树,并为每个节点(前缀)维护一个值表示具有该种前缀的元素数量。而问题的解其实是有限个长度不等的前缀所指向的元素的数量之和。

代码

class Trie {
public:
    int childs[32 * 2 * 10000 + 10][2];
    int size = 0;
    int cnt[32 * 2 * 10000 + 10];
    int pow2[20];
    int bit_max = 18;
    
    Trie()
    {
        memset(childs, 0, sizeof(childs));
        memset(cnt, 0, sizeof(cnt));
        size = 1;
        pow2[0] = 1;
        for (int i = 1; i < 20; i++) 
            pow2[i] = 2 * pow2[i-1];
    }
    
    void insert(int num)
    {
        int cur = 0;
        cnt[cur]++;
        for (int i = bit_max; i >= 0; i--)
        {
            int bit = (pow2[i] & num) > 0;
            if (!childs[cur][bit]) childs[cur][bit] = size++;
            cur = childs[cur][bit];
            cnt[cur]++;
        }
    }
    // 与num异或值小于upper的元素数量. [0, upper)
    int getCnt(int num, int upper)
    {
        if (!upper) return 0;
        int cur = 0, res = 0;
        for (int i = bit_max; i >= 0; i--)
        {
            int bit = (pow2[i] & num) > 0;      // num的第i位
            if (upper > pow2[i])
            {
                if (childs[cur][bit]) res += cnt[childs[cur][bit]];
                upper -= pow2[i];
                if (childs[cur][1-bit]) cur = childs[cur][1-bit];
                else return res;
            }
            else
            {
                if (childs[cur][bit]) cur = childs[cur][bit];
                else return res;
            }
        }
        return res+cnt[cur];
    }
};

class Solution {
public:
    int countPairs(vector<int>& nums, int low, int high) {
        int res = 0;
        Trie trie;
        for (int num:nums)
        {
            res += (trie.getCnt(num, high+1) - trie.getCnt(num, low));
            trie.insert(num);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值