leetcode 307 Range Sum Query
问题描述:给定一序列,求任意区间(i, j)的元素和;修改任意一元素,实现快速更新
树状数组
树状数组的主要特点是生成一棵树,树的高度为logN。每一层的高度为k,分布在这一层的序列元素索引的二进制表达有个共同的特点,就是最低二次幂为k。
子树间有很强的联系,即,给定一序列元素索引i,可以推知该元素所在节点C[i]的父节点C[p],p=i+2^k,其中k=i & (i ^ (i-1)),以及该元素所在子树的前一子树的根节点p=i-2^k。
子树间的联系可以很方便地用于求前n个元素的和,即找到所有子树即可;为实现更新,也只需要logN步操作,实现C[i]值的更新和序列元素的更新。

# leetcode 307
# 树状数组
# 树状数组的本质还是二分树,从而达到O(logN)的算法复杂度
# 树状数组最有趣的地方在于最底层高度为0,序列的第i项的高度为k,k代表i的二进制中从右数0的位数,即2次幂的最低次
# 从二进制的角度来看,很快可以得出结论 2^k = i & (i ^ (i - 1))
# 给出一节点i,可以快速找到它的父节点(i + 2^k)和该节点前的最近一棵子树的根结点(i - 2^k)
# 问题形式:
# 1. 给定一固定序列,求索引n前所有项和:相当于求所有子树的根节点值之和
# 2. 对固定序列任意修改一元素,实现快速更新:相当于更新包含该结点的所有子树的根节点的值
# 最纠结的地方还是求根节点数组C,没想到是通过update求。
class NumArray(object):
def __init__(self, nums):
"""
:type nums: List[int]
"""
self.nums = [0 for _ in nums]
self.n = len(nums)
self.c = [0 for _ in nums]
for i, num in enumerate(nums):
self.update(i, num)
def lowBit(self, x):
## return x & (-x)
return x & (x ^ (x - 1))
def update(self, i, val):
"""
:type i: int
:type val: int
:rtype: void
"""
difference = val - self.nums[i]
self.nums[i] = val
while i < self.n:
self.c[i] += difference
i += self.lowBit(i + 1)
def getSum(self, n):
sum = 0
while n >= 0:
sum += self.c[n]
n -= self.lowBit(n + 1)
return sum
def sumRange(self, i, j):
"""
:type i: int
:type j: int
:rtype: int
"""
return self.getSum(j) - self.getSum(i) + self.nums[i]
# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# obj.update(i,val)
# param_2 = obj.sumRange(i,j)
nums = [1, 3, 5]
X = NumArray(nums)
# X.update(0, 1)
print(X.sumRange(0, 2))
# print(X.getSum(0))
线段树
线段树是一种满二分树,即每个节点的度为0或2.
线段树的主要特点是不断平分区间[s, e]为[s, mid]和[mid + 1, e],其中mid = s + int((e - s) / 2)。称度为0的结点为叶节点,即只包含一个数,不再平分。
线段树的构造是通过构造结点,由其平分性质可知,若给定序列长度为N,则叶节点数目为N,非叶节点为N - 1,即一共2N - 1个结点。
定义根节点的索引为0,其左子树的结点为1, 右子树的结点为2。通用地说,若当前结点为i,则其左结点为(2 * i + 1),右结点为(2 * i + 2),因此,构造结点数目时,索引长度不是N-1。(具体为多少我也还不知道,有博文说是2 * 2 ^(ceil(logN))) - 1,但数据量一大leetcode上就死活通不过)
同样地,线段树可以用于求和和更新。非常巧妙!线段树的操作基本都是基于递归函数,先给一个总区间(也就是根节点对应的区间),然后不断平分,求 getSbSumUntil(ss, mid, qs, qe, (2 * si) + 1) +
getSbSumUntil(mid + 1, se, qs, qe, (2 * si) + 2)
此外,线段树还可以用于求区间和最大和区间替换,下次说。
# leetcode 307
# 线段树
import math
class NumArray(object):
def __init__(self, nums):
"""
:type nums: List[int]
"""
self.nums = nums
self.n = len(nums)
self.st = [0 for _ in range(2 * 2 ^ int(math.log2(self.n)))]
self.constructSTUntil(0, self.n - 1, 0)
def getMid(self, s, e):
return int(s + (e - s) / 2)
def constructSTUntil(self, ss, se, si):
if ss == se:
self.st[si] = self.nums[ss]
return self.st[si]
mid = self.getMid(ss, se)
self.st[si] = self.constructSTUntil(ss, mid, (2 * si) + 1) + \
self.constructSTUntil(mid + 1, se, (2 * si) + 2)
return self.st[si]
def updataUntil(self, ss, se, i, si, difference):
if i < ss or i > se:
return
self.st[si] += difference
if ss != se:
mid = self.getMid(ss, se)
self.updataUntil(ss, mid, i, (2 * si + 1), difference)
self.updataUntil(mid + 1, se, i, (2 * si + 2), difference)
def update(self, i, val):
"""
:type i: int
:type val: int
:rtype: void
"""
difference = val - self.nums[i]
self.nums[i] = val
self.updataUntil(0, self.n - 1, i, 0, difference)
def getSbSumUntil(self, ss, se, qs, qe, si):
if qs == qe:
return self.nums[qs]
if (qs <= ss) and (qe >= se):
return self.st[si]
if (qs > se) or (qe < ss):
return 0
mid = self.getMid(ss, se)
return self.getSbSumUntil(ss, mid, qs, qe, (2 * si) + 1) + \
self.getSbSumUntil(mid + 1, se, qs, qe, (2 * si) + 2)
def sumRange(self, i, j):
"""
:type i: int
:type j: int
:rtype: int
"""
return self.getSbSumUntil(0, self.n - 1, i, j, 0)
# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# obj.update(i,val)
# param_2 = obj.sumRange(i,j)
nums = [-1]
X = NumArray(nums)
X.update(0, 1)
print(X.sumRange(0, 0))
参考文献:
http://bookshadow.com/weblog/search/?pattern=%E7%BA%BF%E6%AE%B5%E6%A0%91&submit=
http://www.cnblogs.com/newpanderking/articles/2775168.html
leetcode 307 Range Sum Query的更多相关文章
- [LeetCode] 307. Range Sum Query - Mutable 区域和检索 - 可变
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- leetcode@ [307] Range Sum Query - Mutable / 线段树模板
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- [LeetCode] 307. Range Sum Query - Mutable 解题思路
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- LeetCode - 307. Range Sum Query - Mutable
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- leetcode 307. Range Sum Query - Mutable(树状数组)
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- [LeetCode] 303. Range Sum Query - Immutable 区域和检索 - 不可变
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- [LeetCode] 304. Range Sum Query 2D - Immutable 二维区域和检索 - 不可变
Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...
- 【刷题-LeetCode】307. Range Sum Query - Mutable
Range Sum Query - Mutable Given an integer array nums, find the sum of the elements between indices ...
- [Leetcode Week16]Range Sum Query - Mutable
Range Sum Query - Mutable 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/range-sum-query-mutable/de ...
随机推荐
- java类的编译、加载和执行
一.java类的编译流程 这里主要讲的是从java文件到class文件 下图是java类编译的详细步骤: 1.词法分析:将java源代码的字符流转变为标记(Token)的集合,Token是编译过程中的 ...
- weblogic优化(内存、线程数和启动速度)
一.为服务分配内存 1.一般如果服务所需的内存不一样,需要单独指定的话,我们是通过拷贝startWebLogic.sh文件,拷贝为startNode.sh(名字随意) 2.然后在startNode.s ...
- 设计模式---接口隔离模式之门面模式(Façade)
前提:接口隔离模式 在组建构建过程中,某些接口之间直接的依赖常常会带来很多问题.甚至根本无法实现.采用添加一层间接接口(稳定的),来隔离本来相互紧密关联的接口是一种常见的解决方案. 典型模式: 门面模 ...
- Golang基础语法1
打开cmd命令窗口 保存,编译,执行: 1.保存到一个×××.go的文件(我这里保存到 E:\GoTest\hello.go 下) 2.编译,在命令提示符中执行命令: go build -o E ...
- vs code解决golang开发环境问题 dial tcp 216.239.37.1:443: connectex: A connection attempt failed
安装插件是出现 如下错误提示, https fetch failed: Get https://golang.org/x/tools/cmd/gorename?go-get=1: dial tcp 2 ...
- ACdream - 1060 递推数(矩阵+循环节)
https://vjudge.net/problem/71677/origin 已知A(0) = 0 , A(1) = 1 , A(n) = 3 * A(n-1) + A(n-2) (n ≥ 2) 求 ...
- ajax传递对象数组
1.Json.stringify()是将json数据格式转换成String类型字符串的方法. 后台可以使用String类型接受,接收完可以使用json转换java集合的方法. List<实体类& ...
- python模块之自定义模块
模块概述 到此之前,我们都是在一个py文件里操作,接下来,我们学习模块的内容,可以从其他文件引入内容(如函数等) 1. 什么是模块 一个py文件就是一个模块,模块是一些相似功能的集合体 2. 为什么要 ...
- C#编程思想(持续更新)
1.将约束的参数先用变量保存,一定不变的设为const,在使用时不直接填入数字而是使用这些变量,这样可以很大程度上方便后续参数的修改 2.字段先用属性封装后,所有的调用都使用属性而不是字段 3.要返回 ...
- extjs.net Combox赋值
1.直接赋值 ].Rows) //遍历获取两个值 { Ext.Net.ListItem listItem = new Ext.Net.ListItem(); //每次创建一个Ext.Net.ListI ...