经典算法:Segment Tree

文章介绍了SegmentTree(分段树)的基本概念、算法实现和优化设计,包括如何在O(logN)的时间复杂度内实现区间值的更新和查询。同时,通过LeetCode2659题目的解题思路和代码展示SegmentTree在实际问题中的应用。

1. Segment Tree简介

Segment Tree,中文名分段树,也算是一个经典的编程算法了,它最常用的场景就是在一些固定但是经常进行值更新的array当中进行区间范围内的某些特征的查询,比如最大值,最小值,区间和等等。

具体来说,对于一个长度固定的array,我们可以对其内部的值进行更新操作,然后也可以对其中某一范围区间的值进行query,比如求和或者求最大值或者最小值。

我们以求和为例,单独对值进行更新或者对范围内求和,其实都可以在 O ( 1 ) O(1) O(1)的复杂度内实现,但是,两者不可兼容。如果我们想要在 O ( 1 ) O(1) O(1)的时间复杂度内更新值,那么范围内求和就会是一个 O ( N ) O(N) O(N)时间复杂度的操作;反之,如果我们使用累积数组,那么我们可以在 O ( 1 ) O(1) O(1)时间复杂度内求取范围和,但是此时更新每一个值就必须同步更新累积数组,就会变成一个 O ( N ) O(N) O(N)复杂度的操作。

因此,如果我们需要同样频繁地更新值以及对范围特征进行query,那么上述方法在时间复杂度上就不可接受了。

而Segment Tree的一个核心思路就是说借用二叉树的结构,将array的每一段区间都保存到二叉树的某一段节点当中,此时,我们无论是对于值的更新还是对于范围的query都可以在 O ( l o g N ) O(logN) O(logN)的时间复杂度内实现,从而达到整体计算效率上的提升。

下图就是网上找的一张典型的最小值求解的Segment Tree的示例图。

在这里插入图片描述

下面,我们就来具体看一下Segment Tree的具体算法实现,即其究竟是如何在 O ( l o g N ) O(logN) O(logN)的时间复杂度内实现值的更新以及范围内特征的query。

2. Segment Tree算法实现

1. 原理说明

如上图所示,Segment Tree的主体就是一个二叉树,其每一个节点都代表着某一个区间范围内的元素性质,然后其左右节点都是对父节点所表示的区间的一个对分,而树的叶子节点就是具体的array当中的某一个具体的值。

因此,每一次对array当中的某一个具体位置上的元素的更新就是一个二分搜索,因此对某一个值的检索的时间复杂度就是 O ( l o g N ) O(logN) O(logN)

但是,每一次对某一个具体的值进行改动,就会相应地影响到其所在的区间的特征值,因此我们需要同步地修改其所在的区间的特征值,我们可以从叶子节点向上追寻到根节点,因此也是一个 O ( l o g N ) O(logN) O(logN)时间复杂度的操作。

最后,我们考察如何在一个范围内求取某一个特征。由于任何范围都可以拆分上述segment tree当中某几个节点的组合,因此,我们只需要迭代找到这些节点然后组合在一起就能够获得我们所需的答案了。

2. vanilla代码实现

我们给出python的segment tree的伪代码实现如下:

class SegmentTreeNode:
    def __init__(self, val, lbound, rbound, lchild=None, rchild=None):
        self.val = val
        self.lbound = lbound
        self.rbound = rbound
        self.lchild = lchild
        self.rchild = rchild

class SegmentTree:
    def __init__(self, arr):
        self.length = len(arr)
        self.root = self.build(0, self.length-1, arr)
        self.vals = arr

    def feature_func(self, lval, rval):
        # get the target feature, such as sum, min or max.
        raise NotImplementError()

    def build(self, lbound, rbound, arr):
        if lbound == rbound:
            root = SegmentTreeNode(arr[lbound], lbound, rbound)
        else:
            mid = (lbound+rbound) // 2
            lchild = self.build(lbound, mid, arr)
            rchild = self.build(mid+1, rbound, arr)
            val = self.feature_func(lchild.val, rchild.val)
            root = SegmentTreeNode(val, lbound, rbound, lchild, rchild)
        return root

    def update(self, idx, val):
        self.vals[idx] = val
        self._update(idx, val, self.root)
        return

    def _update(self, idx, val, root):
        if root.lbound == root.rbound:
            assert(root.lbound == idx)
            root.val = val
            return
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值