浅谈线段树的查找与修改

人生中第一次写博客,有亿点点点紧张,写的不好还望大家海涵(萌新瑟瑟发抖)。

线段树是一棵平衡二叉叉树,树的每个节点代表了一个区间的某个特征(最大值、最小值、或者是区间和)。对于每个节点而言(非叶节点),其两个子节点代表了其左右两个子区间的区间特征,而每个叶节点代表了单独的一个元素(区间左右相等)。其实现方法主要有数组实现和指针实现(本文以数组实现为例)。

先考虑每个节点需要的元素:1.节点数值(毋庸置疑)2.区间左右节点(方便查找)

所以线段树的建树代码如下:

struct tree

{    

    int left,right,value;//分别表示其代表的左右区间以及区间的特征值

}s[800001];

好,接下来我们考虑如何实现线段树的逻辑结构(以区间和为例),其母节点代表一个区间的最大值,而其子节点代表两个子区间的最大值,对于数组[1,2,3,4,5],他的逻辑图应该长这样:

 

越上层的节点对应的区间范围越大。

对于每个节点i其左节点2*i,右节点2*i+1,分别对应于其左子区间[left,(left+right)/2],右区间[(left+right)/2+1,right],直到left==right(叶节点)。

既然这样的话我们该如何建立一棵线段树呢?

考虑递归实现:

void build(int now,int left,int right)

{

    s[now].left=left,s[now].right=right;//明确每个节点的范围

    if(left==right)//找到叶节点

    {

        cin>>s[now].value;//输入叶节点的值并返回

             return;

    }

    int mid=(left+right)/2;

    build(now*2,left,mid);

    build(now*2+1,mid+1,right);//对于非叶节点,递归的建立其左右子节点

    s[now].value =s[now*2].value+s[now*2+1].value;//当前区间和等于左右节点的区间和

}//线段树的建立

那么如果我们想修改一个线段树的节点或者区间该怎么做呢?我们首先要找到对应区间的叶节点,更改其叶节点后再依次对其父节点进行修改:

void update(int now,int left,int right)//left和right代表需要修改区间的左右端点

{

    if(s[now].right<left||s[now].left>right||s[now].mark==1)

    {

        return;

    }//区间越界,终止递归

    if(s[now].left==s[now].right)

    {

cin>>s[now].value;//找到叶节点并进行更改

        return;

 }

    else

    {

        update(2*now,left,right);

        update(2*now+1,left,right);

        s[now].value =s[now*2].value+s[now*2+1].value;//当前区间和等于左右节点的区间和

    }

}

最后的最后,如果我们想要查找线段树的某一区间应该怎么做?

对于本例而言,只需找到区间在线段树的小区间即可(答案存在ans变量里哦):

void find(int now,int left, int right)

{

    if(s[now].left>right||s[now].right<left)

    {

        return;

    }//完全不在区间内,不管了

    if(s[now].left>=left &&s[now].right<=right)//完全在区间里,放心加

    {

       ans+=s[now].value;//将值加到ans上去,下面的就不用找了

return;

    }

    else

    {

        find(2*now,mark);

        find(2*now+1,mark);

    }//递归查找

}

以上就是本萌新带来的线段树的相关知识,希望对大家有所帮助。

最后的最后,附上一道例题,区间最小值小于十为线段树的标记。

样例:输入 #1

 

 

3

5 8

1 420 69 1434 2023

1 2 3

2 2

2 3

2 4

1 2 5

2 1

2 3

2 5

2 3

9999 1000

1 1 2

2 1

2 2

1 1

1

2 1

输出#1:

6

15

1434

1

6

7

36

1

1

再附上一手题目链接:https://www.luogu.com.cn/problem/CF1791F

代码:

#include<iostream>

#include<algorithm>

using namespace std;

struct tree

{    

    int left,right,value,mark;

}s[800001];

void build(int now,int left,int right)

{

    s[now].left=left,s[now].right=right,s[now].mark=0;

    if(left==right)

    {

        cin>>s[now].value;

        if(s[now].value<10)

        {

            s[now].mark=1;

        }

        return;

    }

    int mid=(left+right)/2;

    build(now*2,left,mid);

    build(now*2+1,mid+1,right);

    s[now].mark=s[now*2].mark&&s[now*2+1].mark;

}//线段树的建立

void change(int node)

{

    int temp=s[node].value;int ans=0;

    while(temp>0)

    {

        ans+=(temp%10);

        temp/=10;

    }

    s[node].value=ans;

    if(ans<10)

    {

        s[node].mark=1;

    }

}//线段树的改变

void update(int now,int left,int right)

{

    if(s[now].right<left||s[now].left>right||s[now].mark==1)

    {

        return;

    }

    if(s[now].left==s[now].right)

    {

        //cout<<"&&& "<<s[now].left<<" "<<s[now].right<<endl;

        change(now);

        return;

    }

    else

    {

        update(2*now,left,right);

        update(2*now+1,left,right);

        s[now].mark=s[2*now].mark&&s[2*now+1].mark;

    }

}

void find(int now,int mark)

{

    if(s[now].left>mark||s[now].right<mark)

    {

        return;

    }

    if(s[now].left==s[now].right&&s[now].left==mark)

    {

        cout<<s[now].value<<endl;

        return;

    }

    else

    {

        find(2*now,mark);

        find(2*now+1,mark);

    }

}

int main()

{

    int t;

    cin>>t;

    while(t--)

    {

        int n,q;

        cin>>n>>q;

        build(1,1,n);

        for(int i=0;i<q;i++)

        {

            int temp1=-1,temp2=-1,temp3=-1;

            cin>>temp1;

            if(temp1==1)

            {

                cin>>temp2>>temp3;

                update(1,temp2,temp3);

            }

            if(temp1==2)

            {

                cin>>temp2;

                find(1,temp2);

            }

        }

    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值