- 给你一个整数数组 nums (下标 从 0 开始 计数)以及两个整数:low 和 high ,请返回 漂亮数对 的数目。
漂亮数对 是一个形如 (i, j) 的数对,其中 0 <= i < j < nums.length 且 low <= (nums[i] XOR nums[j]) <= high
输入:nums = [1,4,2,7], low = 2, high = 6
输出:6
解释:所有漂亮数对 (i, j) 列出如下:
- (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
对于这道题可以用字典树来做,也就是在遍历一个数组时候,如遍历到nums[i]时候,先查看字典树里面用多少个数可以与它组成一个漂亮数对,而字典树里面的数字在数组里面的位置是[0 — i -1]的。最后我们再把这个数字nums[i]插入到字典树里面。下面我们分别来讲如何实现:
1.如何把一个数插入字典树里
2.如何知道字典树里面有多少个数字可以与它组成漂亮数对
如何把一个数插入字典树里
首先贴上字典树的代码
private static class TrirNode {
int count;
TrirNode[] child;
public TrirNode() {
count = 0;
child = new TrirNode[2];
}
}
先说明一下为什么要这样设计字典树。因为我们是把一个数字拆分成bit位,字典树存储每个数字的bit位。每个bit位分为0和1
插入字典树的代码
private void insert(int num, TrirNode root) {
for (int i = 15; i >= 0; i--) {
int idx = (num >>> i) & 1;
if (root.child[idx] == null)
root.child[idx] = new TrirNode();
root = root.child[idx];
root.count++;
}
}
因为可以根据数据的范围,我们只存储16位整数。通过对数字的每个bit位来建立树结点。
如何知道字典树里面有多少个数字可以与它组成漂亮数对
我们实现一个函数smller来判断当前的字典树有多少个数与某个值异或小于特定值
代码如下:
private int smaller(TrirNode root, int n, int low) {
int count = 0;
for (int i = 15; i >= 0; i--) {
if (root == null)
return count;
int idx = (n >>> i) & 1;
int lowidx = (low >>> i) & 1;
if (lowidx == 1) {
if (root.child[idx] != null)
count += root.child[idx].count;
root = root.child[1 - idx];
} else {
root = root.child[idx];
}
}
return count;
}
解释一下代码吧。因为我们是在找有多少个数与n异或小于low嘛。那么,怎么知道异或后的值小于low呢,而我们在树里应该怎样走呢?
首先,怎样在树里面走?我们可以根据n异或low之后的值来在树走。
int u = (n >> i) & 1, h = (low >> i) & 1;
- u=0, h=1时
往root.child[0]的这些数肯定都小于hi,因为0 ^ 1 = 1 <= h, 加上
往root.child[1]走 因为 u ^ h = 1 - u=1, h=1时
往root.child[1]的这些数肯定都小于hi,因为 1 ^ 1 = 0 < h,加上
往root.child[0]走 因为 u ^ h = 0 - u=0, h=0时
往root.child[1]肯定是大于h的,因为 0 ^ 0 = 1 > h
往root.child[0]走 - u=1, h=0时
往root.child[0]肯定是大于h的,因为 0 ^ 1 = 0 >= h
往root.child[1]走