对于一道算法题的思考,Leetcode1803. 统计异或值在范围内的数对有多少

  • 给你一个整数数组 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]走
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值