307.区域和检索 - 数组可修改

70 阅读1分钟

题目:
给你一个数组 nums ,请你完成两类查询。

  1. 其中一类查询要求 更新 数组 nums 下标对应的值
  2. 另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的  ,其中 left <= right

实现 NumArray 类:

  • NumArray(int[] nums) 用整数数组 nums 初始化对象
  • void update(int index, int val) 将 nums[index] 的值 更新 为 val
  • int sumRange(int left, int right) 返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的  (即,nums[left] + nums[left + 1], ..., nums[right]
    解法:
    方法一:数状数组 特性参考:www.cnblogs.com/CNLayton/p/… 每个i的父节点:i+lowbit(i),每个父节点j的子节点:j-lowbit(j)。
    树状数组BIT,每个节点保存了区间[i - lowbit(i) + 1, i ] 的区间前缀和。
    主要操作:
  1. Add(index, val),对某个位置增加val。
  2. Get(index) 查询某个位置为结尾(包括)的前缀和。

初始化BIT时,对每个位置进行Add(index, nums[index]),时间复杂度nlog(n)

type NumArray struct {
	Sums []int
	Nums []int
}

// 取x的最后一位
func LowBit(x int) int {
	return x & -x
}

func Constructor(nums []int) NumArray {
	na := NumArray{
		Sums: make([]int, len(nums) + 1), // 从index=1开始使用
		Nums: nums,
	}

	for i := range nums {
		na.Add(i + 1, nums[i])
	}
	return na
}

// index位置增加一个值
func (this *NumArray) Add(index int, val int)  {
	for i := index; i < len(this.Sums); i = i + LowBit(i) {
		this.Sums[i] = this.Sums[i] + val
	}
}

func (this *NumArray) Update(index int, val int)  {
        // NumArray保存Nums,是为了计算Update(index, val)计算增量,作为Add的第二个参数
	this.Add(index + 1, val - this.Nums[index])
	this.Nums[index] = val
}

func (this *NumArray) PrefixSum(index int) int {
	sum := 0
	for ; index > 0; index = index - LowBit(index) {
		sum = sum + this.Sums[index]
	}
	return sum
}

func (this *NumArray) SumRange(left int, right int) int {
	return this.PrefixSum(right + 1) - this.PrefixSum(left) 
}

方法二:bucket思想,和数组前缀和(update O(n),SumRange O(1))相比来减少时间复杂度(达到update O(n/size),SumRange O(1))
分为size个bucket,每个bucket内的数组和保存在bockets中,更新nums[i]时,更新buckets

import "math"
type NumArray struct {
	Buckets []int
	Nums    []int
	Size    int
}

// 取x的最后一位
func LowBit(x int) int {
	return x & -x
}

// 
func Constructor(nums []int) NumArray {
	size := int(math.Sqrt(float64(len(nums))))
	na := NumArray{
		Buckets: make([]int, len(nums) / size + 1), 
		Nums: nums,
		Size: size,
	}

	for i := range nums {
		na.Buckets[i / size] = na.Buckets[i / size] + nums[i]
	}
	return na
}

func (this *NumArray) Update(index int, val int) {
	this.Buckets[index / this.Size] = this.Buckets[index / this.Size] + val - this.Nums[index]
	this.Nums[index] = val
}

func (this *NumArray) SumRange(left int, right int) int {
	b1, b2 := left / this.Size, right / this.Size
	ans := 0
	if b1 == b2 {
		for left <= right {
			ans = ans + this.Nums[left]
			left ++
		}
		return ans
	}

	for l1 := left;   b1 == l1 / this.Size; l1 ++ {
		ans = ans + this.Nums[l1]
	}

	for b1 := b1 + 1; b1 < b2; b1 ++ {
		ans = ans + this.Buckets[b1]
	}

	for l2 := right;  b2 == l2 / this.Size; l2 -- {
		ans = ans + this.Nums[l2]
	}
	return ans
}