今天是郭炜老师讲的线段树和树状数组,我还记得假期时雄哥讲的这个方面,讲的更加深入一些,举了几道例题让我们了解线段树的性质和应用(主要是开辟的结构体内应当存些什么内容),线段树的离散化,以及树状数组的证明问题。讲的例题貌似和雄哥讲的一样。
下面附上自己的代码和讲解:
poj 3264 line up
#include<stdio.h> const int MAX=-9999999; const int MIN= 9999999; struct node{ int left ,right; // 线段树上的区间长度 int nmax,nmin; //每个区间上的最大值和最小值 struct node *pleft,*pright; //左孩子指针,有孩子指针 要注意到,完全二叉树的叶子结点数为n,则总结点的个数为2n-1; } tree[500000]; int count=0; int pmax,pmin; int min(int a,int b){ //比较大小函数 if(a<b)return a; else return b; } int max(int a,int b){ if(a>b)return a; else return b; } void buildtree(struct node *proot,int l,int r) //建树的函数 { proot->left=l; //确定区间的范围 proot->right=r; proot->nmax=MAX; //初始化最大值,最小值 proot->nmin=MIN; if(l!=r){ count++; proot->pleft=tree+count; // 用数组的首地址来表示树 buildtree(proot->pleft,l,(r+l)/2); count++; proot->pright=tree+count; buildtree(proot->pright,(r+l)/2+1,r); //递归的建树 } } void insert(struct node *proot,int num,int i) //插入数据,并对数据进行更新 { if(proot->left==i&&proot->right==i){ // 遇到叶子结点的更新 proot->nmax=num; proot->nmin=num; return ; } proot->nmax=max(proot->nmax,num); proot->nmin=min(proot->nmin,num); // 遇到包含节点的区间的更新 if(i<=(proot->left+proot->right)/2){ insert(proot->pleft,num,i); } if(i>=(proot->left+proot->right)/2+1){ insert(proot->pright,num,i); } } void query(struct node *proot,int b,int e) { if(pmax>=proot->nmax && pmin<=proot->nmin)return; // 一种剪枝的方法 if(proot->left==b&&proot->right==e) { pmax=max(proot->nmax,pmax); //寻找最大值,最小值 pmin=min(proot->nmin,pmin); return; } if(e<=(proot->left+proot->right)/2)query(proot->pleft,b,e); //继续进行遍历 else {if(b>=(proot->left+proot->right)/2+1)query(proot->pright,b,e); else { query(proot->pleft,b,(proot->left+proot->right)/2); query(proot->pright,(proot->left+proot->right)/2+1,e); } } } int main() { int n,q,i,num,s,e; scanf("%d %d",&n,&q); buildtree(tree,1,n); for(i=1;i<=n;i++) { scanf("%d",&num); insert(tree,num,i); } for(i=1;i<=q;i++) { scanf("%d %d",&s,&e); pmax=MAX; // 全局变量记录最大值和最小值 pmin=MIN; query(tree,s,e); printf("%d\n",pmax-pmin); } }