RMQ算法例题(线段树写法)及线段树初学理解

RMQ算法例题(线段树写法)及线段树初学理解

上次学的ST算法解决线段树写法,然后发现线段树其实也可以解决这个问题,不知道为什么我对树有一点阴影,一开始我一直不敢学呜呜呜,还是得慢慢一点点前进!

例题: balanced upline
有n头母牛 接下来一行有n个数表示每头母牛的高度,他们的位置是1-n。
给出T组查询区间,找出区间中(含端点)最小的母牛高度。

如果仅仅是这样,用ST写法可以很快的求出,属于直接套模板的题,稍微给自己加点难度,(正好试着学一下线段树),放一个修改操作。

稍改后的题:
有n头母牛 接下来一行有n个数表示每头母牛的高度,他们的位置是1-n。
现有两个操作分别用0,1表示。
操作0:给出两个数l,r,求出区间【l,r】间最小的母牛高度并输出;
操作1:给出两个数pos,val,将第pos只母牛高度修改成val。
有T行操作,每行操作含三个数,第一个数表示操作。

n<=1e6,1<=l<=r<=n,1<=pos<=n,val<=1e6;

样例输入
5
40 10 20 30 2
6
0 4 5
0 2 4
1 4 6
0 3 4
1 3 4
1 3 20
样例输出
2
10
6

啥也不说,先给代码:

/*the trees will never defeat me forever,
  for all the trees is the scenery on the ACM roads
                                          ————luxun
 */
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define LL long long
#define pb push_back
using namespace std;
const int maxn=1e5+7;
struct node
{
    int l,r,minn;
} tree[maxn];

int arr[maxn];
int n,m,i,j,f,mini,k,T;

//建树
void build(int id,int l,int r)
{
    tree[id].l=l;
    tree[id].r=r;
    if (l==r)
        tree[id].minn=arr[l];//如果区间左右端点重合,则不需要继续分区间
    else
    {
        int mid=(l+r)/2;
        build(id*2,l,mid);//递归建左子树
        build(id*2+1,mid+1,r);//递归建右子树
        tree[id].minn=min(tree[id*2].minn,tree[id*2+1].minn);//父节点的最小值即两个分区间的最小值中较小的值
    }
}

void update(int id,int pos,int val)
{
    if (tree[id].l==pos&&tree[id].r==pos)
    {
        tree[id].minn=val;
        return;
    }
    if (pos>(tree[id].l+tree[id].r)/2) //右子树
    {
        update(id*2+1,pos,val);
    }
    else //左子树
    {
        update(id*2,pos,val);
    }
    tree[id].minn=min(tree[id*2].minn,tree[id*2+1].minn);
}

void search(int id,int l,int r)
{
    if (l<=tree[id].l&&r>=tree[id].r)//不断更新最小值mini
    {
        mini=min(mini,tree[id].minn);
    }
    else
    {
        int k=(tree[id].l+tree[id].r)/2;
        if (l>k)
        {
            search(2*id+1,l,r);
        }
        else
        {
            if (r<=k)
            {
                search(2*id,l,r);
            }
            else
            {
                search(2*id,l,r);
                search(2*id+1,l,r);
            }
        }
    }
}

int main()
{
    cin>>n;
    for (i=1; i<=n; i++)
    {
        cin>>arr[i];
    }
    build(1,1,n);
    cin>>T;
    while(T--)
    {
        cin>>f;
        mini=0x7fffffff;
        if (f)
        {
            int q,w;
            cin>>q>>w;
            update(1,q,w);
        }
        else
        {
            int l,r;
            cin>>l>>r;
            search(1,l,r);
            cout<<mini<<endl;
        }
    }
    return 0;
}

给出自己初学线段树对线段树的理解:

之前的基础树结点基本上只表示一个数,也就是给我们一个一维数组,我们根据维护大小关系或者别的方式维护,最典型的例子是最大堆和最小堆。线段树顾名思义就是维护线段,那么怎么维护呢?

建树:
每一个结点其实是一个结构体,这个结构体里面含有三个数,分别是区间左端点,右端点和这个区间的最小值,也就相当于把区间的最小值都存起来了。如果根结点是【1,n】,我们要求【1,n】中的最小值,就可以求数组前半段【1,mid】和数组后半段【mid+1,n】的最小值,然后取其中较小者作为【1,n】的最小值。如此把每一段区间慢慢分下去,就可以建成一个完整的线段树了。那么比较简单的实现方式就是树论中常见的递归了。在这里插入图片描述

更新树和查找树:
更新树的关键就是找出要修改的树的位置,那么肯定就是找到l=r=pos的地方,用递归查找很容易实现。

查找树的关键就是不断递归,只要【tree[id].l,tree[id].r】区间含在所求区间【l,r】中时,就可以更新【l,r】的最小值,一时更新一时爽,所以一直更新就能更新出最终答案了!
不要忘了这句话 ,很容易遗忘!
在这里插入图片描述
总体来说,基础线段树的理解不难,我相信后期线段树的衍生不会让我失望的(微笑面对生活

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值