线段树(数组实现)

最近看了很多学长发的资料,吸取了别人的优点,把query函数更改的更加合理了。

—————————————————————我是分割线———————————————————————————————————————————— 

 线段树是一棵完美二叉树,树上的每个节点都维护一个区间。根维护的是整个区间,每个节点维护的是父亲的区间二等分后的其中一个子区间。当有n个元素时,对区间的操作可以在O(logn)的时间内完成。

  所以,线段树是擅长处理区间的!

  从网上找了一个图:

  线段树

RMQ(range minimum query)问题:

  在给定数列a0,a1...an,给定s和t,线段树可以求as...at的最值,以求最小值为例,如下图:

  

  每个节点维护对应区间的最小值,例如根节点维护的是下标1到8的最小值,左子树维护的是1到4,右子书维护的是5到6,以此类推。

  建树时,父节点取左右子节点的较小者。我比较喜欢用数组建树,因为非常简单清晰,下面是初始化线段树的代码:

  

1 #define max 999999999
2 int k,n,tree[10005];//n这里是数列里数字的个数
3 void init(){
4     k=1;//k这里是树最后一层的叶子个数
5     while(k<n)//除第一层外,树的每一层都有2的n次方个节点,所以这里求的是k大于n的最小节点数
6         k<<=1;//输入的数字个数n可能小于最下面一层的叶子数
7     for(int i=1;i<=2*k-1;i++)//2*k-1是整个树节点的个数,全部设置为max,反正父节点取的是较小者
8         tree[i]=max;
9 }

  n小于k时,树最后一层的其他值已经是max,所以不影响父节点,下面是线段树每插入一个值的更新:

1 void update(int pos,int val){//pos是树的下标,val是数的值
2     pos+=k-1;//这里数组下标是从1到n,树的下标也是从1到n,这就是叶子节点与数组下标的对应关系,大家可以画一画
3     tree[pos]=val;
4     while(k>1){
5         pos/=2;//父节点
6         tree[pos]=tree[pos*2]<tree[pos*2+1]?tree[pos*2]:tree[pos*2+1];
7     }
8 }

  下面比较关键的是给定下标区间查找,我会在注释里说明:

 1 //调用时query(输入a,输入b,1,1,k)
 2 int query(int a,int b,int pos,int l,int r){//a和b是想要查找的下标区间,l和r是当前节点维护的下标区间,pos是当前节点下标
 3     if(a<=l&&b>=r)//查找终结的条件就是想查找的区间大于节点维护的区间
 4         return tree[pos];
 5     int mid = (l+r)/2;
 6     if(a<=m)
 7         int a = query(a,b,pos*2,l,m);
 8     if(b>m)
 9         int b = query(a,b,pos*2+1,m+1,r);
10     return a<b?a:b;
11 }

 

  OK,到这里,基本的算法就实现了,注意一下树的大小一定要合适。第一篇博客,有些地方想讲清楚可能讲的还不透彻,等我的理解更加深入之后,会继续修改的,如果有错误,希望大家指正,我会虚心接受的~

  

posted @ 2015-10-21 21:48  张秦遥  阅读(1670)  评论(0编辑  收藏  举报