algorithm-segmenttree
20. 线段树-segmentTree
简介
什么是线段树呢?线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
线段树的每个节点都表示一个区间,而根据线段树的不同特征,线段树的节点值可以表示这个区间里的最小值,最大值或者sum值等等。
最小线段树
下面我们以最小线段树为例来说明一下线段树的特性:

如果原始数据是一个数组,我们也以数组来表示线段树。
假设生成的线段树的起点index=1,并且对线段树中的每个非叶子节点index k来说,它的左子节点index=2* k, 而它的右子节点index=2* k+1 。
上图中,标黄色的是原始数组,总共有七个元素。
上面的树形结构就是根据原始数组构建出来的线段树了。
因为是最小线段树,每个非叶子节点存储的都是子节点中的最小值。
举个例子: index=1 的元素表示的是原始数组范围0-6,并且它的值是11,表示的是原始数组0-6范围中,最小的值是11。
11的左子节点表示的范围是0-3,右字节点表示的范围是4-6。
可以看出线段树是一个平衡二叉树(不一定是完全平衡二叉树)。
线段树的叶子节点表示的就是原始数组的每一个值。
注意,这里线段树中范围的表示方式,是通过同时传入treeIndex和数组的左右边界来表示的。
线段树的构建
我们先来看一个线段树构建的动画:

从上面的动画,我们可以看出我们使用的是递归构造的方法。
首先,我们定义两个数组:一个数组存放的是原始数组的引用,一个数组存放的是新生成的线段树。
新构建的segmentTree,对于满二叉树 最后一层的节点数乘以2 大致就是整棵树的节点数。
但是线段树并不一定是满二叉树,但是一定是平衡二叉树,所以需要多冗余一层。也就是 乘以4 就足以盛放所有的节点数。
新构建的segmentTree 以index=1为起点。
我们看一下构建线段树的代码:
我们是递归调用build方法, 直到传入的arrayLeft == arrayRight,也就是说直到该节点是叶子节点的时候,就直接赋值。
如果不是叶子节点,则递归构建左子树和右子树。
最后构建他们的父节点,因为这里我们构建的是最小线段树,所以取两者的最小值。
线段树的搜索
先看一个动画:

上图中,我们搜索的是2-4范围的最小值。
怎么求出来呢?
我们从index=1开始,先判断0-6是否在2-4的范围内,如果不是,则继续搜索左子树和右子树。
搜索左子树:判断0-3是否在2-4的范围内,发现部分重叠,则继续搜索左右子树。
搜索左子树:判断0-1是否在2-4的范围内,发现超出了范围,停止搜索左子树。
搜索右子树:判断2-3是否在2-4范围内,是,则直接返回该节点的值,也就是13。
同样的道理,我们可以得到右节点的返回值是15。
然后比较13和15,得到小的那个13。
用java代码实现如下:
线段树的更新
那么线段树如何更新呢?
我们同样可以使用递归的更新方法,先分别更新左右两个子树,然后再更新他们的父节点。
递归的结束条件是什么呢?
当传入的arrayLeft=arrayRight并且等于要更新的arryIndex的时候,就需要更新叶子节点的segmentTree的值了。
java实现代码如下所示:
同样的道理,我们可以生成最大线段树和Sum线段树。
线段树的复杂度
求线段树的区间统计,时间复杂度和二叉树的高度有关系,和元素的个数没关系,它的时间复杂度为 O(log n)。
本文的代码地址:
本文收录于 www.flydean.com
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
最后更新于
这有帮助吗?