3种算法 - 子数组异或查询


题目

有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]。

对于每个查询 i,请你计算从 Li 到 Ri 的 XOR 值(即 arr[Li] xor arr[Li+1] xor … xor arr[Ri])作为本次查询的结果。

并返回一个包含给定查询 queries 所有结果的数组。

示例 1:

输入:arr = [1,3,4,8], queries = [[0,1],[1,2],[0,3],[3,3]]
输出:[2,7,14,8]
解释:
数组中元素的二进制表示形式是:
1 = 0001
3 = 0011
4 = 0100
8 = 1000
查询的 XOR 值为:
[0,1] = 1 xor 3 = 2
[1,2] = 3 xor 4 = 7
[0,3] = 1 xor 3 xor 4 xor 8 = 14
[3,3] = 8
示例 2:

输入:arr = [4,8,2,10], queries = [[2,3],[1,3],[0,0],[0,3]]
输出:[8,0,4,4]

提示:

1 <= arr.length <= 3 * 10^4
1 <= arr[i] <= 10^9
1 <= queries.length <= 3 * 10^4
queries[i].length == 2
0 <= queries[i][0] <= queries[i][1] < arr.length

解法一

思路:
最直接的思路,就是直接对区间执行两层循环,进行遍历,但实际执行最后一个一个测试用例时超时,代码及测试用例如下(测试用例有525kb,应该是一个29898个数据的数组和2万4千多个[0,29898],太长了,文档中只放了一部分),可以通过这个地址下载查看该测试用例:https://www.zhenxiangsimple.com/files/tech/testCase20200105.txt

空间复杂度:O(1)
时间复杂度:O(k*m),k为queries数组个数,m为数据差值

public class Solution {
    public int[] XorQueries(int[] arr, int[][] queries) {
        int [] r = new int[queries.Length];
        for(int i=0;i<queries.Length;i++)
        {
            int t = 0;
            for(int j=queries[i][0];j<=queries[i][1];j++)
            {
                t = t^ arr[j];
            }
            r[i] = t;
        }
        return r;
    }
}
[64038788,905710003,961785464,588745940,342531091,......,[0,29898],[0,29898],[0,29898],[0,29898],[0,29898]]

解法二

思路:
由于解法一超时,想办法看看能否减少重复计算,当前算法使用空间换时间多思路,先将所有值计算出来,然后查找时候直接索引查找。但是,这种做法对内存消耗太大,对解法一超时多测试用例,当前解法超出了内存空间。
事实上,程序在提交的时候,还没运行到最后一个测试用例,已经内存超出了,测试用例有668Kb,点击链接查看该测试用例:https://www.zhenxiangsimple.com/files/tech/testCase202001051.txt
1,行表示当前计算的数据个数
2,列表示当前计算多起始索引
因此,最终只需要获取数据数和索引即可,代码如下:

空间复杂度:O(N2),N为arr数组数据
时间复杂度:O(N2 + k),k为queries数组个数

public class Solution {
    public int[] XorQueries(int[] arr, int[][] queries) {
        int len = queries.Length, lenA = arr.Length;
        int [,] t = new int[lenA,lenA];
        for(int i=0;i<lenA;i++)
        {//首行
            t[0,i] = arr[i];
        }
        for(int i=1;i<lenA;i++)
        {//之后行
            for(int j=0;j<lenA-i;j++)
            {
                t[i,j] = t[i-1,j] ^ arr[j+i];
            }
        }
        
        int [] r = new int[len];
        for(int i=0;i<len;i++)
        {
            int rIdx=queries[i][1] - queries[i][0];
            r[i] = t[rIdx,queries[i][0]];
        }
        return r;
    }
}

解法三

思路:
使用前缀和的思路,即首先通过前缀累积,然后通过索引对应第值进行相减得到区间值,不过本算是多和改为异或,即进行前缀异或算法,索引N存储前N个数的异或值,并通过两个位置的和相减得到区间值。
对于异或来讲,加法和减法都是执行异或操作,即A xor A = 0或者 A xor B xor A = B。
代码如下:
空间复杂度:O(N),N为arr数组数据
时间复杂度:O(N+k),k为queries数组个数

public class Solution {
    public int[] XorQueries(int[] arr, int[][] queries) {
        int len=queries.Length, lenA = arr.Length;
        int [] t = new int[lenA+1];
        t[0] = 0;
        for(int i=1;i<=lenA;i++)
        {
            t[i] = t[i-1] ^ arr[i-1];
        }
        
        int [] r = new int[len];
        for(int i=0;i<len;i++)
        {
            r[i] = t[queries[i][1]+1] ^ t[queries[i][0]];
        }
        return r;
    }
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

放羊郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值