返回
数据结构修炼:化繁为简,破解 leetcode 307 区域和检索问题
前端
2023-09-29 05:46:03
各位程序员朋友们,大家好!今天,我们继续踏上数据结构的修炼之旅,破解 leetcode 中颇具挑战的 307 号问题——区域和检索 - 数组可修改。
问题引述
leetcode 307 号问题如下:
给定一个整数数组 nums 和一个查询列表 queries,其中每个查询都是一个整数对 [start, end]。对于每个查询,返回子数组 nums[start, end] 的和。
理解问题
这个问题的核心在于高效查询子数组的和。需要注意的是,数组中的元素是可以修改的,这使得问题更具挑战性。
线段树初识
为了解决这个问题,我们可以引入一种名为线段树的数据结构。线段树是一种树形数据结构,可以高效地存储区间信息和进行区间查询。线段树的每个节点代表一个区间,并存储该区间的相关信息,例如区间和。
构建线段树
要构建线段树,我们可以采用自底向上的方式。首先,我们把每个元素作为线段树的叶节点。然后,我们将相邻的叶节点合并为父节点,父节点存储这两个叶节点区间的和。这个过程一直持续到根节点,根节点代表整个数组的区间。
更新操作
当数组中的元素发生变化时,我们只需要更新受影响的线段树节点。我们可以使用类似二分查找的方法快速定位受影响的节点,然后更新节点的值以及所有祖先节点的值。
查询操作
要查询子数组的和,我们可以使用线段树的查询操作。我们只需要找到包含查询区间的线段树节点,就可以直接获取子数组的和。
实现示例
以下是实现线段树解决 leetcode 307 问题的 Python 代码示例:
class SegmentTree:
def __init__(self, nums):
n = len(nums)
self.tree = [0] * (4 * n)
self.build(nums, 0, 0, n - 1)
def build(self, nums, node, start, end):
if start == end:
self.tree[node] = nums[start]
else:
mid = (start + end) // 2
self.build(nums, 2 * node + 1, start, mid)
self.build(nums, 2 * node + 2, mid + 1, end)
self.tree[node] = self.tree[2 * node + 1] + self.tree[2 * node + 2]
def update(self, index, val, node, start, end):
if start == end:
self.tree[node] = val
else:
mid = (start + end) // 2
if index <= mid:
self.update(index, val, 2 * node + 1, start, mid)
else:
self.update(index, val, 2 * node + 2, mid + 1, end)
self.tree[node] = self.tree[2 * node + 1] + self.tree[2 * node + 2]
def query(self, left, right, node, start, end):
if start > right or end < left:
return 0
if start >= left and end <= right:
return self.tree[node]
mid = (start + end) // 2
left_sum = self.query(left, right, 2 * node + 1, start, mid)
right_sum = self.query(left, right, 2 * node + 2, mid + 1, end)
return left_sum + right_sum
nums = [1, 3, 5, 7, 9, 11]
segment_tree = SegmentTree(nums)
print(segment_tree.query(1, 3)) # 15
segment_tree.update(2, 2, 0, 0, len(nums) - 1)
print(segment_tree.query(1, 3)) # 16
结语
通过使用线段树,我们有效地解决了 leetcode 307 问题,实现了对数组子数组和的快速查询和更新。希望今天的分享能帮助大家进一步理解线段树的原理及其在解决实际问题的应用。