力扣周赛221期 1707 与数组中元素的最大异或值 (JAVA 字典树 贪心 离线算法)

1707. 与数组中元素的最大异或值

难度 困难 

给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries ,其中 queries[i] = [xi, mi] 。

第 i 个查询的答案是 xi 和任何 nums 数组中不超过 mi 的元素按位异或(XOR)得到的最大值。换句话说,答案是 max(nums[j] XOR xi) ,其中所有 j 均满足 nums[j] <= mi 。如果 nums 中的所有元素都大于 mi,最终答案就是 -1 。

返回一个整数数组 answer 作为查询的答案,其中 answer.length == queries.length 且 answer[i] 是第 i 个查询的答案。

 

示例 1:

输入:nums = [0,1,2,3,4], queries = [[3,1],[1,3],[5,6]]
输出:[3,3,7]
解释:
1) 0 和 1 是仅有的两个不超过 1 的整数。0 XOR 3 = 3 而 1 XOR 3 = 2 。二者中的更大值是 3 。
2) 1 XOR 2 = 3.
3) 5 XOR 2 = 7.
示例 2:

输入:nums = [5,2,4,6,6,3], queries = [[12,4],[8,1],[6,3]]
输出:[15,-1,5]
 

提示:

1 <= nums.length, queries.length <= 105
queries[i].length == 2
0 <= nums[j], xi, mi <= 109

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

 第220期周赛中科普了一下离线算法,没想到这一周就用到了,关于离线算法可以看下这个链接:https://blog.csdn.net/lang_a/article/details/81204180

这道题的思路和和 421. 数组中两个数的最大异或值 一致,多了一步离线排序的过程

421题的思路如下:

思路分析:这种题就不要暴力法,指名道姓说要O(n)。根据提示需要使用建树。
首先我们需要知道,二进制高位为1会大于低位的所有和,比如"11111111"最高位代表的"1"按权展开为128,
而后面的“1111111”按权展开的和也只是127。所以进行异或时应该尽量选择高位异或结果为“1”的。
第一步:遍历数组,我们按照二进制[31,30,…,1, 0]各位的状态进行建树,left放置0,right放置1。
比如某个int型数的二进制是"0110110…",我们需要将其放置到[left,right,right,left,right,right,left…]。
第二步:遍历数组,按照贪心策略,尽量维持当前选择的方向能保证当前能位异或结果为1。
--------------------- 
作者:hestyle 
来源:CSDN 
原文:https://blog.csdn.net/qq_41855420/article/details/88756998 
版权声明:本文为博主原创文章,转载请附上博文链接!

有了上面的分析很容易就可以想到这题是对queirs针对limit进行排序,然后对nums排序,遍历排序后的queirs取出nums中小于当前limit的数字建立字典树,贪心的方式进行每一次选择即可得到所有的查询结果。

JAVA实现如下:

421题:

class Solution {
        class Node {
        public Node left;
        public Node right;

        public Node() {
        }
    }
    public int findMaximumXOR(int[] nums) {
        // 二叉树模拟 字典树,左为1,右为0
        // 树初始化
        Node root = new Node();
        // 小于2^30时 使用30位作为掩码
        int mask = 1 << 31;
        for (int i : nums) {
            String s = Integer.toBinaryString(mask | i);
            Node cur = root;
            // 30位分解
            for (char c : s.toCharArray()) {
                if (c == '0') {
                    if (cur.right == null) {
                        cur.right = new Node();
                    }
                    cur = cur.right;
                } else {
                    if (cur.left == null) {
                        cur.left = new Node();
                    }
                    cur = cur.left;
                }
            }
        }
        int max = Integer.MIN_VALUE;
        // 遍历树,贪心规则 每遍历一位时尽量XOR为1
        for (int i : nums) {
            String s = Integer.toBinaryString(mask | i);
            Node cur = root;
            StringBuilder xor = new StringBuilder();
            for (char c : s.toCharArray()) {
                if (c == '0') {
                    if (cur.left != null) {
                        cur = cur.left;
                        xor.append("1");
                    }else{
                        cur = cur.right;
                        xor.append("0");
                    }
                } else {
                    if (cur.right != null) {
                        cur = cur.right;
                        xor.append("1");
                    }else{
                        cur = cur.left;
                        xor.append("0");
                    }
                }
            }
            int tem = Integer.parseInt(xor.toString(),2);
            max = Math.max(max,tem);
        }
        return max;
    }
}

1707题:

class Solution {
    class Node {
        public Node left;
        public Node right;

        public Node() {
        }
    }
    public int[] maximizeXor(int[] nums, int[][] queries) {
        // 存放结果
        Map<Query, Integer> hm = new HashMap<>();
        // 离线排序
        Query[] queries1 = new Query[queries.length];
        for (int i = 0; i < queries.length; i++) {
            queries1[i] = new Query(queries[i][0], queries[i][1]);
        }
        Arrays.sort(queries1, new Comparator<Query>() {
            @Override
            public int compare(Query o1, Query o2) {
                return o1.limit - o2.limit;
            }
        });
        // 初始化字典树,并贪心求值
        Arrays.sort(nums);
        // 树初始化
        Node root = new Node();
        // 小于10^9 使用31位作为掩码
        int mask = 1 << 31;
        int index = 0;
        for (int i : nums) {
            while (index<queries.length&&i > queries1[index].limit) {
                // 遍历树,贪心规则 每遍历一位时尽量XOR为1
                int tem = getMaxXorVal(root, mask, queries1[index].val);
                hm.put(queries1[index], tem);
                index++;
            }
            String s = Integer.toBinaryString(mask | i);
            Node cur = root;
            // 30位分解
            for (char c : s.toCharArray()) {
                if (c == '0') {
                    if (cur.right == null) {
                        cur.right = new Node();
                    }
                    cur = cur.right;
                } else {
                    if (cur.left == null) {
                        cur.left = new Node();
                    }
                    cur = cur.left;
                }
            }
        }
        int[] answer = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            if (hm.containsKey(new Query(queries[i][0], queries[i][1]))) {
                answer[i] = hm.get(new Query(queries[i][0], queries[i][1]));
            } else {
                answer[i] = getMaxXorVal(root, mask, queries[i][0]);
            }
        }
        return answer;
    }

    private int getMaxXorVal(Node root, int mask, int val) {
        String s = Integer.toBinaryString(mask | val);
        Node cur = root;
        if (root.left == null && root.right == null) {
            return -1;
        }
        StringBuilder xor = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (c == '0') {
                if (cur.left != null) {
                    cur = cur.left;
                    xor.append("1");
                } else {
                    cur = cur.right;
                    xor.append("0");
                }
            } else {
                if (cur.right != null) {
                    cur = cur.right;
                    xor.append("1");
                } else {
                    cur = cur.left;
                    xor.append("0");
                }
            }
        }
        return Integer.parseInt(xor.toString(), 2);
    }

    class Query {
        int val;
        int limit;

        public Query(int val, int limit) {
            this.val = val;
            this.limit = limit;
        }

        @Override
        public int hashCode() {
            return val + limit;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Query) {
                Query obj2 = (Query) obj;
                if (obj2.limit == this.limit && obj2.val == this.val) {
                    return true;
                }
            }
            return false;
        }
    }
}

执行结果:

421:

1707:

 

注:时间复杂度上满足要求,但实现上仍有可剪枝和降低系数的方式,如421题每个数字求异或值时可以判断是否已经比max小,如果是则可以剪枝。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值