1 概述
线段树(Segment Tree)是一种用来处理区间问题的数据结构,可以O(logN)的时间复杂度下实现单点查询、单点修改、区间查询、区间修改等功能。
比如说有一个长度为N的数列,有2类操作:
1、将某个区间中的数都加上x。
2、询问某个区间中所有数的和。
现在有M个操作,对于每个第二类操作,输出一个数表示答案。
上面这一道题如果暴力搞得话时间复杂度是O(NM)的,数据一大就承受不了了,但是如果使用线段树的话就可以在O(MlogN)的时间内解决本题。那么接下来我们就来讲一讲线段树究竟是如何构造与实现的。
2 思想&代码实现
2.1 建树
举个例子,现在有一个数列:1,5,4,3,2,7,8,6。现在我们要根据这个数列去构造一棵表示区间和的线段树,那么这棵树会是这样子的:
其中,节点旁的红色字表示当前这一个节点所表示的是哪一个区间的区间和,节点中的数字则表示这一个节点所表示的区间和是多少。
通过上图,我们可以发现线段树是一棵二叉树,对于每一个节点(除了叶节点),都将其表示的区间一分为二交给它的左右儿子来处理,并且每个节点(除了叶节点)都等于它的两个儿子的权值之和。我们建树其实也可以利用这个思路,将建树的过程总结为两点:
1.如果这一个节点不是叶节点的话,那么就将其代表的区间分成两半丢给左右儿子处理。
2.如果这一个节点是叶节点的话,就直接赋值(因为叶节点所表示的区间只有一个元素了嘛)。
说到这里有人可能会问了,我们应该怎么去储存线段树每一个节点的左右儿子呢?解决这个问题的常用方法是用数组按层存储。那么什么是“按层存储”呢?顾名思义,就是一层层地存储。就拿上面这棵树来说吧,其中每个节点在数组中的编号如下图所示:
先看代码吧:
void build_tree(int p,int l,int r)//建树,表示此时的节点是p,所代表的区间为[l,r]时的情况 { if(l==r)//如果只剩一个数,就说明当前节点为叶节点 { tree[p]=a[l];///直接赋值 return;//结束 } int mid=(l+r)/2; build_tree(p*2,l,mid);//处理左儿子 build_tree(p*2+1,mid+1,r);//处理右儿子 tree[p]=tree[p*2]+tree[p*2+1];//当前节点的值就等于左右儿子的值之和 }
(应该都能看懂吧)
未完待续.......