一个数组的异或和是指数组中所有的数异或在一起的结果,给定一个数组arr,求最大子数组异或和。

news/2024/7/7 19:22:24

问题描述:

        一个数组的异或和是指数组中所有的数异或在一起的结果,给定一个数组arr,求最大子数组异或和。

异或运算规则:

        异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。

异或运算法则为:

        a⊕a=0;a⊕b=b⊕a;a⊕b⊕c=a⊕(b⊕c)=(a⊕b)⊕c;

        d=a⊕b⊕c可以推出a=d⊕b⊕c;a⊕b⊕a=b。

        ^也可以表示特殊的二元运算符——逐位逻辑运算符(用于对数据的位进行操作),它表示的含义是逐位异或(xor),要求两个整型操作数。从最小(即最右)的位开始,对操作数逐位操作;如果其中两个数不同则为1,相同则为0。例如:

        x →   0000 0000 1011 1111

        y →    0000 1111 0101 1111

        x^y → 0000 1111 1110 0000

思路:

思路一:查找出数组的全部子数组(时间复杂度为:O(N^2)),遍历每一个子数组(时间复杂度为:O(N)),即可查找出最大子数组的异或和。算法的时间复杂度为O(N^3)。

思路二:利用预处理数组求出以每个位置结尾时,从0位置到结尾位置的异或和,由于eor(i) = eor(0~j) ^ eor(j+1~i) --> eor(j+1~i) = eor(0~i) ^ eor(0~j)即可以加速求异或和。我们可以以j位置结尾,0~j,1~j,2~j,...,i~j,分别求出异或和取最大值,每个j位置都求一遍找到最大子数组异或和。算法的时间复杂度为O(N^2)。

思路三: 利用前缀树(把树转化为二进制)。前缀树种有所有从0位置开始到i-1位置的前缀树记录,当要求以i位置结尾时的最大子数组前缀和时,我们不知道前面从哪个位置开始和i位置的数异或起来是最大的,我们可以求出现在从0位置到i位置的异或和,然后在前缀树中利用贪心帮我们找一个最大的前缀和。下面是利用前缀树求的代码。如果最高位是0,那么他希望遇到和他相反的数1,后面的位都希望遇到相反的数,那么该位还是1,如果最高位是1那么希望遇到那么希望遇到1让他最高位变为0成为一个整数,后续希望遇到和他相反的数成为1.时间复杂度O(N)

代码

思路二代码

    public static int maxXorSubarray1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int[] eor = new int[arr.length];
        eor[0] = arr[0];
        for (int i = 1; i < arr.length; i++) {
            eor[i] = eor[i - 1] ^ arr[i];
        }
        int max = Integer.MIN_VALUE;
        for (int j = 0; j < arr.length; j++) {
            for (int i = 0; i <= j; i++) {
                max = Math.max(max, i == 0 ? eor[j] : eor[j] ^ eor[i - 1]);
            }
        }
        return max;
    }

思路三代码

    public static class Node {
        public Node[] nexts = new Node[2];
    }

    public static class NumTrie {
        public Node head = new Node();

        //把某个数字newNum加入到这棵树的前缀树中
        //num是一个32位的整数,所以加入的过程一共走32步
        public void add(int newNum) {
            Node cur = head;
            for (int move = 31; move >= 0; move--) {
                //从高位到低位,取出每一位的状态,如果当前状态是0,path(int) = 0
                //如果当前状态是1,path(int) = 1
                int path = ((newNum >> move) & 1);
                //无路新建,有路复用
                cur.nexts[path] = cur.nexts[path] == null ? new Node() : cur.nexts[path];
                cur = cur.nexts[path];
            }
        }

        //该结构之前收集了一些数字,并且建好了前缀树
        //eroi 和 ? ^ 最大结果返回
        public int maxXor(int eori) {
            Node cur = head;
            int res = 0;
            for (int move = 31; move >= 0; move--) {
                int path = (eori >> move) & 1;
                //期待的路

                //      若eori是正数,则符号位(最高位)为0,返回结果最大我们就需要一个数的符号位(最高位)也为0
                //      若eori是负数,则符号位(最高位)为1,返回结果最大我们就需要一个数的符号位(最高位)也为1
                //最高位上我总是期待与你原来的一样
                //   我们其他位置上不管正数还是负数我们尽可能保证从高位到低位最多的1
                //      正数我们很好理解,负数符号位为1,若其他位置全为1,则这个二进制数字表示-1。(负数十进制变二进制需要取反加一)
                //其他位置上我期待与你相反
                int best = move == 31 ? path : (path ^ 1);
                //实际走的路
                //若期待的路存在则有期待的路
                //期待的路不存在则走相反的路
                best = cur.nexts[best] != null ? best : (best ^ 1);
                //(path ^ best ) 当前位位异或完的结果
                res |= (path ^ best) << move;
                cur = cur.nexts[best];
            }
            return res;
        }
    }

    public static int maxXorSubarray2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int max = Integer.MIN_VALUE;
        //0~i   异或和
        int eor = 0;
        NumTrie numTrie = new NumTrie();
        //一个数也没有的时候,异或和是0
        numTrie.add(0);
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];// 0...i  异或和
            //X  0~0  0~1  ...  0~i1
            max = Math.max(max, numTrie.maxXor(eor));
            numTrie.add(eor);
        }
        return max;
    }

测试代码

    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) ((maxValue + 1) * Math.random());
        }
        return arr;
    }

    public static int[] copyArray(int[] arr) {
        if (arr == null) {
            return null;
        }
        int[] res = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            res[i] = arr[i];
        }
        return res;
    }

    public static void printArray(int[] arr) {
        if (arr == null) {
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int maxSize = 20;
        int maxValue = 20;
        int testTime = 200000;
        System.out.println("测试开始!");
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxValue);
            int[] arr2 = copyArray(arr1);
            int max1 = maxXorSubarray1(arr1);
            int max2 = maxXorSubarray2(arr2);
            if (max1 != max2) {
                System.out.println("出现错误了!");
                printArray(arr1);
                printArray(arr2);
                System.out.println(max1);
                System.out.println(max2);
                break;
            }
        }
        System.out.println("测试结束!");
    }


http://lihuaxi.xjx100.cn/news/163925.html

相关文章

Tilemap瓦片资源

1、Tilemap Tilemap一般称之为 瓦片地图或者平铺地图&#xff0c;是Unity2017中新增的功能&#xff0c;主要用于快速编辑2D游戏中的场景&#xff0c;通过复用资源的形式提升地图多样性 工作原理就是用一张张的小图排列组合为一张大地图 它和SpriteShape都是用于制作2D游戏的…

Ajax笔记

Ajax笔记资源的请求方式一、概念1、Ajax作用2、jQuery中的Ajax二、$.get()函数的语法$.get()发起不带参数的请求$.get()发起带参数的请求三、$.post()函数的语法$.post()向服务器提交数据<font colorred>四、$.ajax()函数的语法使用$.ajax()发起GET请求使用$.ajax()发起P…

VMware虚拟机安装黑苹果步骤与常见问题,VMware16,MacOS12.01(Moterey)

资源准备&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1JFtpMVrULiky9l3SvCXX-w 提取码&#xff1a;c452 说明&#xff1a; 1.镜像版本10.14和12.01根据需要选择其一即可&#xff0c;10.14的后缀为cdr&#xff0c;12.01的后缀为ISO&#xff0c;这两种文件VMware都支…

JavaScript之BOM复习(54th)

1、BOM概述 1、BOM Browser Object Model 浏览器对象模型 2、它提供了独立于内容而与浏览器窗口进行交互的对象&#xff0c;其核心对象是 window 3、BOM 由一系列相关的对象构成&#xff0c;并且每个对象都提供了很多方法与属性 4、BOM 缺乏标准&#xff0c;JavaScript 语法的…

Lingo软硬件划分 实例

文章目录一、SM2 加密算法软硬件划分1.1 实验目标1.2 实验过程&#xff08;1&#xff09; 综合考虑使得系统整体性能最&#xff08;2&#xff09;只考虑硬面积&#xff0c;即系统硬件面积最小&#xff08;3&#xff09;只考虑功耗&#xff0c;即系统功耗最小&#xff08;4&…

nodejs校园二手交易管理系统vue

本系统的设计主要是为给网上用户提供购物方便&#xff0c;所以应该完成以下目标&#xff1a; (1) 登录、注册。用户要想在交易系统中购买商品&#xff0c;就必须先登录系统。如果不是会员&#xff0c;就必须先注册&#xff0c;然后才能登录系统。 (2) 查找商品。用户可以查找自…

opencv 空域变换

图像变换是基于像素的映射&#xff0c;区别是像素是怎么映射的。灰度变换的话是通过点对点的映射&#xff0c;也就是变换后的像素点之和当前的像素点有关&#xff08;gramma变换、对数变换等等&#xff09;&#xff0c;依次来进行对比度拉伸。而空间滤波变换后的像素点是和当前…

NX二次开发-调内部函数SEL_set_type_filter_index_by_label设置类型过滤器例子剖析怎么查找内部函数调用内部函数

NX二次开发-调内部函数SEL_set_type_filter_index_by_label设置类型过滤器例子剖析怎么查找内部函数调用内部函数 前言 给那些不会调内部函数的人,一个学习方法,大概知道怎么找内部接口,怎么调用内部函数的。 复杂的东西我也不会,等我研究出来了,在更新到博客上。 版本…