数据结构学习——线段树

线段树作为一种二叉搜索树,在处理区间问题上大大提高了效率。

首先我们以一个数组为例构建线段二叉树的示意图:

数组为 a[1...5] = {5, 4, 2, 3, 1}

                                                [1,  5]

                                            /               \

                                       [1, 3]               [4, 5]

                                    /           \           /           \

                               [1, 2]        [3, 3]    [4, 4]       [5, 5]

                               /    \

                         [1, 1]   [2, 2]


如上示意图所示, 每个结点内的主要信息就是数组a的下标范围,如此, 在求区间最值与区间和就十分方便了。

在线段树的操作中,最基本就是构造线段树,以及更新线段树。

下面以求区间和与求区间最小值为例。

首先确定线段树的数据结构:

struct SegTree //使用静态树的方法来保存信息
{
    int left, right; // 结点所示区间[l, r]
    int minv;//最小值
    int sum;//区间和
};


SegTree stree[maxn];//自行定义maxn
然后是建立区间树的函数:

void build(int v, int l, int r) // v为所用的stree节点, l,r表示范围
{
    stree[v].left = l;
    stree[v].right = r;
    if(l == r)
    {//区间内只有一个值时,最小值和区间和便确定了
        stree[v].sum = A[l];
        stree[v].minv = A[l];
    }
    else
    {//递归构造,并更新sum与minv
        int mid = (l + r) / 2;
        build(2 * v, l, mid);
        build(2 * v + 1, mid + 1, r);
        stree[v].sum = stree[v*2].sum + stree[v*2+1].sum;
        stree[v].minv = min(stree[v*2].minv, stree[v*2+1].minv);
    }
}
build(1, 1, n);

线段树更新:

void update(int id, int pos, int x) //id为其实stree的下标,pos为要更新a[i]的下标, x为要更新的值
{
    if(stree[id].left == stree[id].right)//区间只有一个数时,直接完成更新
    {
        A[stree[id].left] = x;
        stree[id].minv = x;
        stree[id].sum = x;
    }
    else
    {    //根据 l, r分别向左右进行搜索
	int mid = (stree[id].left + stree[id].right) / 2; 
        if(pos <= mid) update(id * 2, pos, x); 
        else update(id * 2 + 1, pos, x);
        stree[id].sum = stree[id*2].sum + stree[id*2+1].sum;
        stree[id].minv = min(stree[id*2].minv, stree[id*2+1].minv);
    }
}
update(1, pos, v);



求区间和:

int findSum(int root, int l, int r)

{
    if(stree[root].left >= l && stree[root].right <= r) // 当节点区间包含在[l, r] 中时,直接返回区间和
    {
        return stree[root].sum;
    }
    else
    {//分别计算左右子树的区间和
        int sumL = 0, sumR = 0;
        int mid = (stree[root].left + stree[root].right) / 2;
        if(l <= mid)
            sumL = findSum(root * 2, l, r);
        if(r > mid)
            sumR = findSum(root * 2 + 1, l, r);
        return sumL + sumR;
    }
}



求区间最小值:

int findmin(int root, int l, int r) // root为树根, l, r为指定区间
{
    if(stree[root].left >= l && stree[root].right <= r) //区间中只有一个数时,直接返回
    {
        return stree[root].minv;
    }
    else
    {//分别查找左右子树
        int minL = INF, minR = INF;
        int mid = (stree[root].left + stree[root].right) / 2;
        if(l <= mid)
            minL = findmin(root * 2, l, r);
        if(r > mid)
            minR = findmin(root * 2 + 1, l, r);
        return min(minL, minR);//返回最小值
    }
}

运用以上代码,就基本能在O(log2n)时间内完成相应的操作,提高查找效率。根据要求不同,也可修改对应的数据结构来满足需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值