概念:
线段树是一种特殊的结构,它每个节点记录着一个区间和这个区间的一个计数,表示此区间出现的次数。
线段树分为构造build部分,插入insert部分,以及查询query部分。其主要的思想就是用空间换时间,来使一些特殊的问题的时间复杂度减少。比如对于一段空间或者一个数字的出现次数,以线段树来查询可以使时间复杂度从乘法减少到log,具体的复杂度分析可以看参考资料1:http://blog.csdn.net/bochuan007/article/details/6713971
举个栗子:
根节点为1-7区间的线段树如下所示:
它build的规则是:根节点部分表示的区间是所有数据的[min,max]部分,令mid = (min + max) / 2,左孩子代表的区间是[min,mid],右孩子代表的区间是[mid + 1, max]。
叶子节点代表的是单个数字,即left == right。
那么我们首先定义一个数据结构,表示线段数中节点元素:
struct Element{//元素结构体维护一个计数、一个左边界和右边界
int count = 0;
int left;
int right;
Element(int vx, int vy) :left(vx), right(vy){}//构造函数
};
然后,我们再对二叉树本身写一个结构,其中包含了上面所述的元素:
struct Node{//线段树的结点,里面有一个元素结构体以及孩子指针
Element *e;
Node* lchild;
Node* rchild;
Node(Element *ve) : e(ve), lchild(NULL), rchild(NULL){}
};
那么,线段数的build部分的函数如下:
Node* build(int n, int m){//构造树的方法,输入左、右边界和父结点指针
Element *te = new Element(n, m);//构造元素结构体
Node *ret = new Node(te);//构造返回结点
if (n == m){//说明是叶子节点
return ret;
}
else{//递归构造左右孩子节点
int mid = (n + m) / 2;
ret->lchild = build(n, mid);
ret->rchild = build(mid + 1, m);
}
return ret;
}
接下来:线段数的insert插入规则如下:对于插入的区间[n,m],如果线段数节点当前表示区间[l,r]刚好覆盖了[n,m],那么当前节点的count添加上insert的计数部分。
否则,让子节点去接收。
注意,只有当当前节点恰好符合时才会接收,否则不会接收。
举个栗子,比如上面的1-7的线段树,插入[1,4]计数为5的元素,