线段树(数组实现)

最近看了很多学长发的资料,吸取了别人的优点,把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,到这里,基本的算法就实现了,注意一下树的大小一定要合适。第一篇博客,有些地方想讲清楚可能讲的还不透彻,等我的理解更加深入之后,会继续修改的,如果有错误,希望大家指正,我会虚心接受的~

  

转载于:https://www.cnblogs.com/zqy123/p/4899197.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值