線段樹入門詳解:從基礎(chǔ)到應(yīng)用
线段树是一种高效的数据结构,特别适用于处理静态或动态数组的区间查询和更新操作。它能够在较短的时间内完成对数组内某些区间的查询和更新操作。线段树的基本思想是将原数组划分成多个区间,并为每个区间创建一个节点,以树状结构表示。线段树的应用场景非常广泛,包括区间最大值/最小值查询、区间和查询、区间更新等。
线段树简介线段树是一种高效的数据结构,特别适用于处理静态或动态数组的区间查询和更新操作。它能够在较短的时间内完成对数组内某些区间的查询和更新操作。线段树的基本思想是将原数组划分成多个区间,并为每个区间创建一个节点,以树状结构表示。每个节点代表一个区间,而每个叶子节点代表原数组中的一个元素。
线段树的应用场景
常见的应用场景包括:
- 区间最大值/最小值查询:给定一个数组,查询某个区间内的最大值或最小值。
- 区间和查询:给定一个数组,查询某个区间内的元素之和。
- 区间更新:给定一个数组,更新某个区间内所有元素的值。
- 区间排序:利用线段树进行区间内元素的排序操作。
- 前缀和后缀查询:查询数组的前缀或后缀和。
线段树的节点结构
一个线段树节点通常包含以下几个属性:
start
:该节点表示的区间的起点。end
:该节点表示的区间的终点。sum
:该节点表示的区间的和。left
:该节点的左子节点。right
:该节点的右子节点。
class Node:
def __init__(self, start, end):
self.start = start
self.end = end
self.sum = 0
self.left = None
self.right = None
线段树的递归构建过程
线段树的构建过程可以使用递归实现。首先创建一个根节点,该节点表示整个数组的区间。接下来,递归地创建左右子节点,每个子节点表示原区间的一半。当区间长度为1时,递归过程结束。
def build_tree(start, end):
node = Node(start, end)
if start == end:
return node
mid = (start + end) // 2
node.left = build_tree(start, mid)
node.right = build_tree(mid + 1, end)
return node
线段树的基本操作
查询操作详解
在查询操作中,我们通常需要计算某个区间内的元素之和。当查询的区间完全包含节点表示的区间时,可以使用节点的sum
属性。当查询的区间完全不包含节点表示的区间时,结果为0。否则,递归地向左右子节点查询。
def query(node, q_left, q_right):
if q_left <= node.start and node.end <= q_right:
return node.sum
if q_right < node.start or node.end < q_left:
return 0
return query(node.left, q_left, q_right) + query(node.right, q_left, q_right)
更新操作详解
在更新操作中,我们通常需要更新某个区间内所有元素的值。当更新的区间完全包含节点表示的区间时,直接更新节点的sum
属性。当更新的区间完全不包含节点表示的区间时,不需要进行任何操作。否则,递归地向左右子节点更新。
def update(node, index, value):
if node.start == node.end:
node.sum = value
return
mid = (node.start + node.end) // 2
if index <= mid:
update(node.left, index, value)
else:
update(node.right, index, value)
node.sum = node.left.sum + node.right.sum
线段树的优化技巧
线段树的延迟更新
在某些情况下,我们可以将更新操作延迟到真正需要使用时进行,以提高更新效率。具体来说,当需要更新某个区间时,如果该区间完全包含于某个节点表示的区间,我们可以将更新操作向下传递,并标记该节点为“待更新”。当真正需要使用节点的sum
属性时,再进行更新操作。
class LazyNode:
def __init__(self, start, end):
self.start = start
self.end = end
self.sum = 0
self.lazy = 0
self.left = None
self.right = None
def push_down(node):
mid = (node.start + node.end) // 2
if node.left is None:
node.left = LazyNode(node.start, mid)
if node.right is None:
node.right = LazyNode(mid + 1, node.end)
if node.lazy:
node.left.sum += node.lazy * (mid - node.start + 1)
node.right.sum += node.lazy * (node.end - mid)
node.left.lazy += node.lazy
node.right.lazy += node.lazy
node.lazy = 0
def update(node, q_left, q_right, value):
if q_left <= node.start and node.end <= q_right:
node.sum += value * (node.end - node.start + 1)
node.lazy += value
return
push_down(node)
mid = (node.start + node.end) // 2
if q_left <= mid:
update(node.left, q_left, q_right, value)
if q_right > mid:
update(node.right, q_left, q_right, value)
node.sum = node.left.sum + node.right.sum
def query(node, q_left, q_right):
if q_left <= node.start and node.end <= q_right:
return node.sum
push_down(node)
mid = (node.start + node.end) // 2
res = 0
if q_left <= mid:
res += query(node.left, q_left, q_right)
if q_right > mid:
res += query(node.right, q_left, q_right)
return res
线段树的区间合并
在线段树中,我们可以通过合并左右子节点的结果来计算整个区间的属性。这种方法可以避免重复计算,提高查询和更新效率。
class Node:
def __init__(self, start, end):
self.start = start
self.end = end
self.children = []
def merge(node):
if node is None or len(node.children) == 0:
return
node.sum = sum([child.sum for child in node.children])
for child in node.children:
merge(child)
线段树实例分析
实际问题的线段树解决方法
举个简单的例子,假设我们有一个长度为n
的数组,需要频繁地对数组中的某个区间进行查询或更新操作。我们可以使用线段树来实现高效的操作。具体的解决方案如下:
- 构建线段树,将数组的每个元素作为叶子节点。
- 对于每个区间查询操作,使用查询操作计算该区间内的元素之和。
- 对于每个区间更新操作,递归地更新每个区间内所有元素的值。
线段树代码实现示例
下面是线段树的完整代码实现。这段代码实现了线段树的构建、查询和更新操作。
class Node:
def __init__(self, start, end):
self.start = start
self.end = end
self.sum = 0
self.left = None
self.right = None
def build_tree(start, end):
node = Node(start, end)
if start == end:
return node
mid = (start + end) // 2
node.left = build_tree(start, mid)
node.right = build_tree(mid + 1, end)
return node
def query(node, q_left, q_right):
if q_left <= node.start and node.end <= q_right:
return node.sum
if q_right < node.start or node.end < q_left:
return 0
return query(node.left, q_left, q_right) + query(node.right, q_left, q_right)
def update(node, index, value):
if node.start == node.end:
node.sum = value
return
mid = (node.start + node.end) // 2
if index <= mid:
update(node.left, index, value)
else:
update(node.right, index, value)
node.sum = node.left.sum + node.right.sum
# 示例
n = 10
arr = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
tree = build_tree(0, n - 1)
for i in range(n):
update(tree, i, arr[i])
print(query(tree, 2, 5)) # 输出 21
update(tree, 5, 11)
print(query(tree, 2, 5)) # 输出 26
线段树练习题推荐
入门级练习题
- 给定一个长度为
n
的数组,实现区间查询和更新操作。 - 实现一个区间最大值查询功能。
- 实现一个区间最小值查询功能。
进阶级练习题
- 实现一个区间排序功能。
- 实现一个前缀和后缀查询功能。
- 对线段树进行优化,实现延迟更新和区间合并功能。
共同學(xué)習(xí),寫下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章