线段树总结

来源:互联网 发布:淘宝旗袍销量第一名 编辑:程序博客网 时间:2024/06/06 14:00

解决的题目:对区间所对应的一些数据进行修改,查询。

基本步骤:先建树,然后插入数据,然后更新,查询。

关键部分:用线段树解题,关键是要想清楚每个节点要存哪些信息以及这些信息如何高效更新,维护,查询。

                 不要一更新就更新到叶子节点,那样更新效率最坏就可能变成O(n)的了。


建树的方式:

1)数组 

若根节点下标为0。假设线段树上某节点下标为i, 则:左子节点下标为 i *2+1,右子节点下标为 i*2+2

如果用一维数组存放线段树,且根节点区间[1,n]。那么

->使用左右节点指针,则数组需要有2n-1个元素

->不使用左右节点指针,则数组需要有:2*2^ [log2n] -1个元素 ([log2n]向上取整)2*2^ [log2n] -1 <= 4n -1 , 实际运用时常可以更小,可尝试 3n

struct CNode{int L,R;int data;int Mid(){return (L+R)/2;}}tree[maxn*3];   //3*n就够了 

或者

struct CNode{   int L,R;int data;CNode *pLeft,*pRight;int Mid(){return (L+R)/2;}}tree[maxn*2];



更新方式:

#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>using namespace std;const int maxn=50000+5;struct Node{int L,R,mid;//题目中要求的特殊变量 Node *pLeft,*pRight;Node(int L=0,int R=0,/*初始化特殊变量*/,Node *pLeft=NULL,Node *pRight=NULL):L(L),R(R),LeftNum(L){mid=(L+R)/2;}}tree[maxn*2];int nNode;void BuildTree(Node *root,int s,int e){*root=Node(s,e);if(s==e) return;root->pLeft=++nNode+tree;root->pRight=++nNode+tree;BuildTree(root->pLeft,s,root->mid);BuildTree(root->pRight,root->mid+1,e);}void Add(Node *root,int s,int e){if(root->L==s&&root->R==e){//更新特殊变量 return;}if(e<=root->mid)        Add(root->pLeft,s,e);else if(s>=root->mid+1) Add(root->pRight,s,e);else {Add(root->pLeft,s,root->mid);Add(root->pRight,root->mid+1,e);}//更新特殊变量 }void Delete(Node *root,int s,int e){if(root->L==s&&root->R==e){//更新特殊变量 return;}if(e<=root->mid)        Delete(root->pLeft,s,e);else if(s>=root->mid+1) Delete(root->pRight,s,e);else {Delete(root->pLeft,s,root->mid);Delete(root->pRight,root->mid+1,e);}//更新特殊变量 }

其中结点中的特别变量要特别注意:

1)要保存求题目相关的数据

2)要方便区间合并   如,POJ 3667,其中保存的LeftMax和RightLen就是用来方便在合并区间的时候更新MaxLen

3)为了加快速度,常常放置一个标志,不然就是O(n)复杂度  如POJ 3667 中的Occupied标志


注意由于3),所以常常不能更新到叶子结点,所以在用数据的时候要根据标记更新其他数据

题目POJ 3667地址:http://blog.csdn.net/qq_34446253/article/details/52370039


0 0
原创粉丝点击