LeetCode——1803. 统计异或值在范围内的数对有多少(Count Pairs With XOR in a Range)[困难]——分析及代码(Java)

LeetCode——1803. 统计异或值在范围内的数对有多少[Count Pairs With XOR in a Range][困难]——分析及代码[Java]

一、题目

给你一个整数数组 nums (下标 从 0 开始 计数)以及两个整数:low 和 high ,请返回 漂亮数对 的数目。
漂亮数对 是一个形如 (i, j) 的数对,其中 0 <= i < j < nums.length 且 low <= (nums[i] XOR nums[j]) <= high 。

示例 1:

输入:nums = [1,4,2,7], low = 2, high = 6
输出:6
解释:所有漂亮数对 (i, j) 列出如下:
    - (0, 1): nums[0] XOR nums[1] = 5 
    - (0, 2): nums[0] XOR nums[2] = 3
    - (0, 3): nums[0] XOR nums[3] = 6
    - (1, 2): nums[1] XOR nums[2] = 6
    - (1, 3): nums[1] XOR nums[3] = 3
    - (2, 3): nums[2] XOR nums[3] = 5

示例 2:

输入:nums = [9,8,4,2,1], low = 5, high = 14
输出:8
解释:所有漂亮数对 (i, j) 列出如下:
    - (0, 2): nums[0] XOR nums[2] = 13
    - (0, 3): nums[0] XOR nums[3] = 11
    - (0, 4): nums[0] XOR nums[4] = 8
    - (1, 2): nums[1] XOR nums[2] = 12
    - (1, 3): nums[1] XOR nums[3] = 10
    - (1, 4): nums[1] XOR nums[4] = 9
    - (2, 3): nums[2] XOR nums[3] = 6
    - (2, 4): nums[2] XOR nums[4] = 5

提示:

  • 1 <= nums.length <= 2 * 10^4
  • 1 <= nums[i] <= 2 * 10^4
  • 1 <= low <= high <= 2 * 10^4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-pairs-with-xor-in-a-range
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、分析及代码

1. 暴力算法

(1)思路

直接暴力计算,利用 num^num2=i 等效于 num^i=num2 的特点,先统计当前各个数字出现的次数,再将当前数字和 [low, high] 范围内的数字进行异或运算,将其结果对应的出现次数相加。

(2)代码

class Solution {
    
    
    public int countPairs(int[] nums, int low, int high) {
    
    
        int ans = 0;
        int[] freq = new int[20001];//nums[i]<=20000
        Arrays.fill(freq, 0);
        for (int num : nums)//记录每个数字出现的次数
            freq[num]++;
        //暴力算法
        for (int num : nums) {
    
    
            for (int i = low; i <= high; i++) {
    
    
                int num2 = num ^ i;//num^i=num2 <-> num^num2=i
                if (num2 <= 20000) 
                    ans += freq[num2];//num^num2=i中当前num对应num2出现的次数
            }
            freq[num]--;//当前num所有组合已统计,减去出现次数,避免重复
        }
        return ans;
    }
}

(3)结果

执行用时 :2476 ms,在所有 Java 提交中击败了 5.18% 的用户;
内存消耗 :39.9 MB,在所有 Java 提交中击败了 84.91% 的用户。

2. 字典树(Trie树)

(1)思路

在上述算法的基础上,结合字典树方法快速统计。在依次将 nums 中数字加入字典树的同时,搜索和该数字异或值在 [0, high] 和 [0, low - 1] 范围内数字 num2 的个数并相减,就是符合异或值为 [low, high] 区间内的数字个数。

(2)代码

class TrieNode {
    
    //字典树节点
    int cnt;//节点数字个数统计值
    TrieNode[] child;//2个子节点对应0和1

    TrieNode() {
    
    
        this.cnt = 0;
        child = new TrieNode[2];
    }
}

class Solution {
    
    
    public int countPairs(int[] nums, int low, int high) {
    
    
        int ans = 0, maxDigit = 14;//2^15=32768,15位二进制足够计算
        TrieNode root = new TrieNode();//不包含数值的根节点
        for (int num : nums) {
    
    
            //此时对nums[j],字典树统计范围只包括索引<=j的nums,无重复统计
            ans += search(root, maxDigit, num, high) - search(root, maxDigit, num, low - 1);
            //添加num进字典树
            TrieNode node = root;
            for (int i = maxDigit; i >= 0; i--) {
    
    //逐位添加
                int numI = (num >> i) & 1;
                if (node.child[numI] == null)
                    node.child[numI] = new TrieNode();
                node = node.child[numI];
                node.cnt++;
            }
        }
        return ans;
    }

    //搜索和num异或值在[0,range]范围内的数字num2的个数
    public int search(TrieNode node, int digit, int num, int range) {
    
    
        if (node == null)
            return 0;
        if (digit < 0)
            return node.cnt;
        int numI = (num >> digit) & 1, rangeI = (range >> digit) & 1;//num和range在该位的值
        if (rangeI == 1)//range在该位为1
            if (numI == 0)//num在该位为0,num2该位为0的部分全部满足,为1的部分继续判断
                return (node.child[0] == null) ? search(node.child[1], digit - 1, num, range) : node.child[0].cnt + search(node.child[1], digit - 1, num, range);
            else//num在该位为1,num2该位为1的部分全部满足,为0的部分继续判断
                return (node.child[1] == null) ? search(node.child[0], digit - 1, num, range) : node.child[1].cnt + search(node.child[0], digit - 1, num, range);
        if (numI == 0)//range在该位为0,num2该位必须和num一致
            return search(node.child[0], digit - 1, num, range);
        return search(node.child[1], digit - 1, num, range);
    }
}

(3)结果

执行用时 :112 ms,在所有 Java 提交中击败了 81.25% 的用户;
内存消耗 :45.6 MB,在所有 Java 提交中击败了 36.66% 的用户。

三、其他

暂无。

猜你喜欢

转载自blog.csdn.net/zml66666/article/details/115254006