线段树基本操作

线段树基本操作
线段树的思想来自分治思想。首先求解两个子问题,问题的答案由两个子问题的答案得出。
例如求一个区间的最值。可分别求出左半区间的最值,再求出右半区间的最值,区间的最值为两个最值的最值。而再求左半区间和右半区间时,又可以用分治。直到最后问题化简为最简单的问题,即区间只有一个数的情况。这时,最值便是他本身。
线段树就是这种数据结构。可用于解决区间问题。
线段树的叶子节点就是最简单的情况,即只有一个数的区间。
线段树的父节点表示的区间是两个子区间的并。负责维护这个区间的最值。
线段树最上面一个节点,维护这个区间的最值。
如图所示[1,8] 表示区间
第一个节点维护的是整个区间[1,8]的最值。
同理,[1,1] 维护的区间[1,1]的最值。即他本身;

线段树

#include <bits/stdc++.h>

using namespace std;

struct Node		//每个节点的结构
{
    int Left;		//维护的区间的左边
    int Right;		//维护的区间的右边
    int Val;			//区间[Left, Right] 的最值。
};

struct Node SegTree[100*2-1];
int a[100] = {0,10,9,2,5,7,1,3,4};
// 刚开始传入的k为1,这是利用数组来建树,第一个节点的下标为1,第二个为2,
//第三个为3。第k个节点的左孩子的下标为 k*2 ,右孩子为 k*2+1
void Build(int k, int L, int R) //建树
{
    SegTree[k].Left = L;
    SegTree[k].Right = R;

    if(L == R)	//当L=R时说明是叶子节点,到了最简单的情况。
    {
        SegTree[k].Val = a[L];
        return;
    }

    int Mid = (L+R)/2;
    Build(k<<1, L, Mid);		//递归建立左子树
    Build(k<<1 | 1, Mid+1, R);	//递归建立右子树
    SegTree[k].Val = max(SegTree[k<<1].Val, SegTree[k<<1 | 1].Val);	
    //节点的最值为左右子树的最值的最值。
}
//刚开始传入的k为1
void Updata(int k, int dir, int num) //更新,
//传入的是区间中第几个位置,以及要更新的值。
{							
		//例如 序列{0,10,9,2,5,7,1,3,4};
		//Update(1,3,11) 表示将序列的第三个值改为11.
    if(SegTree[k].Left == SegTree[k].Right)	
    //如果left=right表示找到了最简单的区间,即只有一个数的区间。则更新该值。
    {
        SegTree[k].Val = num;
        return;
    }
    int Mid = (SegTree[k].Left+SegTree[k].Right)/2;	
    //判断更新的值在左右子树的哪一边,再递归更新那一个区间。
    if(dir <= Mid)	
    //要更新的值的位置比当前区间中间的位置小,说明在左边区间,即左子树。
        Updata(k<<1, dir, num);
    if(dir > Mid)		//同理
        Updata(k<<1 | 1, dir, num);

    SegTree[k].Val = max(SegTree[k<<1].Val, SegTree[k<<1 | 1].Val);	
    //更新值之后要更新包含该区间的所有区间的最值。
}
//刚开始的k等于1
//查询的思想是将查询的区间转换成树中已经存在的区间。
//例如查询区间[1,8] 由于树中已存在区间[1,8],所以直接返回区间范围为[1,8]的节点的值即
//可。即第一个节点的值。
//例如查询区间为[1,4]。与上面的情况区别不大。找到[1,4]的节点,直接返回即可。
//但是如果要查询的区间不能再树中找到,例如[1,3]
//此时应该找到区间[1,2], 区间[3,3] 查询的最值为这两个区间的最值的最值。
//同理查询区间[2,5],要返回区间[2,2] , [3,4], [4,4] 三个值的最值。
//至于怎么寻找这些子区间可利用当前节点的区间与要查询的区间进行比较得出。
//例如查询区间[2,5]
//刚开始首节点的区间为整个区间[1,8], (1+8)/2 = 4; 说明左子树表示的区间为[1,4],
//右子树表示的区间为[5,8] .因为区间[2,5] 与这两个区间都有交集。所以递归查询左右子 
//树。 当递归到区间[3,4] 时发现,区间[3,4] 整个包含在区间[2,5]中。所以直接返回区间  
//[3,4]//的值。(区间的值为这个区间的最值)。
int Query(int k, int L, int R) //查询区间[L,R]的最值
{
    if(L <= SegTree[k].Left && SegTree[k].Right <= R)
        return SegTree[k].Val;

    int Mid = (SegTree[k].Left+SegTree[k].Right)/2;

    int mins = -1;

    if(L <= Mid)
        mins = max(mins, Query(k<<1, L, R));
    if(R > Mid)
        mins = max(mins, Query(k<<1 | 1, L, R));

    return mins;
}

//第一个示例。以查询区间最大值为例
int main(void)
{
    Build(1, 1, 8);

    Updata(1, 1, 11);

    int tmp = Query(1,2,5);

    cout << tmp << endl;

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值