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% 的用户。
三、其他
暂无。