今天是 Kevin 的算法之路100天征程的第4天。为大家讲解 LeetCode 第 307 题,是一道中等难度的题目,这道题是上一篇的升级版,不了解的可以去看看上一篇。
区域和检索 - 数组不可变
一个人在沙漠里快要饿死了,这时他捡到了神灯。
神灯:" 我只可以实现你一个愿望,快说吧,我赶时间。"
人: “我要老婆 ……”
神灯立刻变出一个美女,然后不屑的说:" 都快饿死了还贪图美色!可悲! “说完就消失了。
人:”……饼。 "
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。
示例:
Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
说明:数组仅可以在 update 函数下进行修改。
你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/range-sum-query-mutable
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
同样的,我们可以用暴力法直接刚,很简单粗暴,就不多解释了。
// Go
type NumArray struct {
data []int
}
func Constructor(nums []int) NumArray {
return NumArray{nums}
}
// 优秀的你应该发现了与上题相比,就是多了这个 Update 方法
func (this *NumArray) Update(i int, val int) {
this.data[i] = val
}
func (this *NumArray) SumRange(i int, j int) int {
sum := 0
for l := i; l <= j; l++ {
sum += this.data[l]
}
return sum
}
// Java
class NumArray {
private int[] data;
public NumArray(int[] nums) {
data = nums;
}
public int sumRange(int i, int j) {
int sum = 0;
for (int l = i; l <= j; l++) {
sum += data[l];
}
return sum;
}
public void update(int i, int val) {
data[i] = val;
}
}
在上篇文章中已经说过暴力法最大的缺点就是耗时,上一题是通过缓存,以空间换时间来提升效率。那么这一题我们想想改怎么做呢?
建议大家先动脑筋想一想哦~ 是不是也可以用空间换时间呢?
答案是可以的
并且我们可以构造一种特殊的数据结构——线段树,线段树常用来处理数组相应的区间查询(range query)和元素更新(update)操作。线段树算是一种比较高级的树结构,这里作为了解即可。
这题使用的是树状数组。树状数组和线段树很像,但能用树状数组解决的问题,基本上都能用线段树解决,而线段树能解决的树状数组不一定能解决。相比较而言,树状数组效率要高很多。
相关介绍可以看下这个网站:https://oi-wiki.org/ds/fenwick/
// go
type NumArray struct {
s_tree, nums []int
}
func Constructor(nums []int) NumArray {
newArray := &NumArray{make([]int, len(nums) + 1), nums}
for i, num := range nums {
newArray.s_update(i, num)
}
return *newArray
}
func (this *NumArray) Update(i int, val int) {
this.s_update(i, val - this.nums[i])
this.nums[i] = val
}
func (this *NumArray) SumRange(i int, j int) int {
return this.s_sum(j + 1) - this.s_sum(i)
}
func (this *NumArray) s_update(i, val int) {
// 树状数组的标准写法,i & -i 是获取二进制的最小的一个1,然后用i减去这个1所在数字
for i, n := i + 1, len(this.nums); i <= n; i += i & -i {
this.s_tree[i] += val
}
}
func (this *NumArray) s_sum(i int) (res int) {
for ; i > 0; i -= i & -i {
res += this.s_tree[i]
}
return
}
郑重声明:
所展示代码已通过 LeetCode 运行通过,请放心食用~
很多人都想养成好习惯,但大多数人却是三分钟热度。为了我们能一起坚持下去,决定制定如下计划(福利)
一起学算法,打卡领红包!
参与方式:
关注我的原创微信公众号「Kevin的学堂」,一起学习,一起更优秀!
打卡规则为:
「留言」“打卡XXX天” ➕「分享」到朋友圈
奖励:
连续打卡
21
天,联系本人获取6.6
元红包一个!连续打卡
52
天,联系本人获取16.6
元红包一个!连续打卡
100
天,联系本人获取66.6
元红包一个!