线段树(Segment Tree)(也叫区间树)是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+b)/2],右子树表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树。叶节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
另外还有如zkw线段树的特种线段树。
线段树构造
首先介绍构造线段树的方法:让根节点表示区间[0,N-1],即所有N个数所组成的一个区间,然后,把区间分成两半,分别由左右子树表示。不难证明,这样的线段树的节点数只有2N-1个,是O(N)级别的,如图:
线段树的维护:
#include<iostream> using namespace std; typedef struct Node{ int left,right; struct Node* leftChild; struct Node* rightChild; int min,max,sum; Node():min(0),max(0),sum(0){} }Node; void build(Node* curNode,int l,int r) { curNode->left=l,curNode->right=r; if(l==r) { curNode->leftChild=curNode->rightChild=NULL; } else { curNode->leftChild=new Node(); curNode->rightChild=new Node(); int mid=(l+r)/2; build(curNode->leftChild,l,mid); build(curNode->rightChild,mid+1,r); } } void query(Node* curNode,int l,int r) { // // 如果当前结点的区间包含在查询区间内 if(l<=curNode->left && curNode->right<=r) { cout<<curNode->left<<"--->"<<curNode->right<<endl; } else { int mid=(curNode->left+curNode->right)/2; if(l<=mid)// 和左孩子有交集,考察左子结点 query(curNode->leftChild,l,r); if(r>mid) query(curNode->rightChild,l,r); } } void modify(Node* curNode,int x,int num) { Node* LC=curNode->leftChild; Node* RC=curNode->rightChild; if(curNode->left==curNode->right)//对叶节点进行处理 { curNode->min=num; curNode->max=num; curNode->sum=num; } else { int mid=(curNode->left+curNode->right)/2; if(x<=mid) modify(LC,x,num); else if(x>mid) modify(RC,x,num); curNode->sum=LC->sum+RC->sum;//上推 if(LC->max > RC->max) curNode->max=LC->max; else curNode->max=RC->max; if(LC->min < RC->min) curNode->min=LC->min; else curNode->min=RC->min; } } int querySum(Node* curNode,int l,int r) { Node* LC=curNode->leftChild; Node* RC=curNode->rightChild; int ret=0; if(l<=curNode->left && curNode->right<=r) ret=curNode->sum; else { int mid=(curNode->left+curNode->right)/2; if(l<=mid) ret += querySum(LC,l,r); if(r>mid) ret += querySum(RC,l,r); } return ret; } int main() { int n=5; Node* root=new Node(); build(root,0,n-1); modify(root,0,100); query(root,2,3); }
输出
2--->2
3--->3
请按任意键继续. . .
如果是:
modify(root,0,100);
modify(root,2,100);
modify(root,3,100);
cout<<querySum(root,0,2)<<endl;结果为:200.
#include<iostream> using namespace std; struct Node { int left,right; //区间左右值 Node *leftChild; Node *rightChild; int cover; Node():cover(0){} }; void build(Node* node,int l,int r) { node->left=l; node->right=r; if(l==r) node->leftChild=node->rightChild=NULL; else { node->leftChild=new Node(); node->rightChild=new Node(); int mid=(l+r)/2; build(node->leftChild,l,mid); build(node->rightChild,mid+1,r); } } void insert(Node* node,int l,int r) { if(l<=node->left && node->right<=r) node->cover++; else { int mid=(node->left+node->right)/2; if(l<=mid) insert(node->leftChild,l,r); if(r>mid) insert(node->rightChild,l,r); } } /* 给线段树每个节点增加一个域cover。cover=1表示该结点所对应的区间被完全覆盖,cover=0表示该结点所对应的区间未被完全覆盖。 */ void insert2(Node* node,int l,int r) { if(node!=NULL && node->cover==0) { int mid=(node->left+node->right)/2; if(l==node->left && r==node->right) node->cover=1; else if(r<=mid) insert2(node->leftChild,l,r); else if(l>=mid) insert2(node->rightChild,l,r); else { insert2(node->leftChild,l,r); insert2(node->rightChild,l,r); } } } int count(Node* node) { int m,n; if(node->cover==1) return (node->right-node->left+1); else if(node->right-node->left==1) return 1; m=count(node->leftChild); n=count(node->rightChild); return m+n; } void deleteLine(Node* node,int l,int r) { if(l<=node->left && node->right<=r) node->cover--; else { int mid=(node->left+node->right)/2; if(l<=mid) deleteLine(node->leftChild,l,r); if(r>mid) deleteLine(node->rightChild,l,r); } } int main() { int n=8; Node * node=new Node(); build(node,1,8); insert2(node,1,4); insert2(node,2,5); insert2(node,7,8); cout<<count(node)<<endl; }
上面的insert和insert2不同,这里判断区间重合我们用到了insert2。
count看cover有没有设置为1.则直接返回node.right - node.left+1,其子树不用管.
更多;
http://blog.csdn.net/metalseed/article/details/8039326
http://blog.csdn.net/tianshuai1111/article/details/7828961
http://blog.csdn.net/yangtrees/article/details/8262594
http://www.notonlysuccess.com/index.php/segment-tree-complete/