返回

用LeetCode 0303题实现区域和查询——数组不变性下的突破

前端





**前言** 

LeetCode 0303题“区域和检索 - 数组不可变”是一道经典的算法题,也是程序员面试的常见题目。这道题要求我们在数组不可变的情况下,高效地查询数组中某一段范围内的元素和。

**前缀和** 

前缀和是一种简单而有效的解决办法。它通过预先计算数组中每个元素的前缀和,使得查询某一段范围内的元素和只需要计算相应前缀和之间的差值。

**差分** 

差分是一种与前缀和类似的方法,但它存储的是相邻元素之间的差值。通过这种方式,查询某一段范围内的元素和只需要计算相应差值的和。

**树状数组** 

树状数组是一种二进制索引树,它可以高效地查询和更新数组中的元素。在树状数组中,每个元素存储的是其自身的值以及其子孙节点的和。通过这种方式,查询某一段范围内的元素和只需要计算相应节点的和。

**比较** 

这三种方法各有优缺点。前缀和的实现简单,查询时间复杂度为O(1),但更新时间复杂度为O(n)。差分的实现也比较简单,查询时间复杂度为O(1),更新时间复杂度为O(1)。树状数组的实现相对复杂,查询时间复杂度为O(log n),更新时间复杂度为O(log n)。

**代码示例** 

```python
# 前缀和
def prefix_sum(nums):
    n = len(nums)
    pre_sum = [0] * (n + 1)
    for i in range(1, n + 1):
        pre_sum[i] = pre_sum[i - 1] + nums[i - 1]
    return pre_sum

def range_sum(pre_sum, i, j):
    return pre_sum[j + 1] - pre_sum[i]

# 差分
def difference(nums):
    n = len(nums)
    diff = [0] * n
    for i in range(1, n):
        diff[i] = nums[i] - nums[i - 1]
    return diff

def range_sum(diff, i, j):
    return diff[j] - diff[i - 1] + nums[i - 1]

# 树状数组
class BinaryIndexedTree:
    def __init__(self, nums):
        n = len(nums)
        self.tree = [0] * (n + 1)
        for i in range(1, n + 1):
            self.update(i, nums[i - 1])

    def update(self, i, val):
        while i <= len(self.tree):
            self.tree[i] += val
            i += i & -i

    def query(self, i):
        sum = 0
        while i > 0:
            sum += self.tree[i]
            i -= i & -i
        return sum

def range_sum(tree, i, j):
    return tree.query(j + 1) - tree.query(i)

# 时间复杂度对比
def time_complexity(n):
    print("Prefix sum:")
    print("  Query: O(1)")
    print("  Update: O(n)")
    print("Difference:")
    print("  Query: O(1)")
    print("  Update: O(1)")
    print("Binary indexed tree:")
    print("  Query: O(log n)")
    print("  Update: O(log n)")

总结

LeetCode 0303题“区域和检索 - 数组不可变”是一道经典的算法题,它要求我们在数组不可变的情况下,高效地查询数组中某一段范围内的元素和。文章介绍了三种常见的解法:前缀和、差分以及树状数组,并对每种方法的原理、实现和复杂度进行了详细的分析。同时,文章还给出了这三种方法的代码示例和时间复杂度对比,以帮助读者更好地理解和选择合适的方法。