当前位置: 首页 > news >正文

线段树原理和代码详解

目录

线段树维护的信息类型

线段树的结构

线段树的初始化

线段树的功能:

单点修改,区间查询

区间修改,区间查询


以下内容均为个人见解,如有不足还请指出,作者会及时修改!

期待大家的点赞、收藏、评论!!!诸君共勉!!!

线段树维护的信息类型

 父范围上的某个信息,可以用O(1)的时间,从子范围的信息加工得到。比如:累加和、最大值、最小值。而像某个区间内出现次数最多的数就不行。

线段树的经典功能,如下操作单次调用的时间复杂度是O(logN)

1.范围查询,包括范围内累加和、最大值、最小值等信息。

2.范围修改,包括范围内每个数都增加、重置等操作。

线段树的组织(以最经典的累加和为列)

1.线段树一般以1为最开始的下标

2.线段树在初始化时,就指定范围的数据规模[1,n],一旦确定范围不能修改

3.任何一个大范围的[l,r],可以通过其中点mid,拆分成左范围[l,mid]和右范围[mid+1,r]

线段树的结构

struct Node
{
    int l, r;表示范围左右边界
    int sum = 0;//表示该范围的累加和

    //Int lz=0;懒标记,区间修改用到
};

线段树的初始化

Node tree[N << 2];//线段树的数组表示形式
int arr[N];//原数据数组
void build(int p, int l, int r)
{
    tree[p] = { l,r };//左右边界初始化
    if (l == r)//叶子结点,范围内只有一个数据
    {
        tree[p].sum = arr[l];
        return;
    }
    int mid = l + r >> 1;

    //将范围分成左右两个范围
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);

    tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
}

这里有个问题,如果线段树的数据范围是1~n,那么线段树的数组需要开多大呢?答案是4n

推理过程:

范围为1~n的数据,形成满二叉树的高度最高是(logn+2),那么这颗二叉树的结点数就是2^h=2^(logn+2)=4n 

线段树的功能:

单点修改,区间查询

1.单点修改:除了要对目标点进行修改,还要对路过的所有点进行更新

如上图所示,对3位置数据+1,还要对3~4范围数据+1,1~4范围数据+1

void add(int p, int x, int k)
{
    if (tree[p].l == tree[p].r)//说明找到该点
    {
        tree[p].sum += k;
        return;
    }
    if (tree[p << 1].r >= x)
        add(p << 1, x, k);
    else
        add(p << 1 | 1, x, k);
    
    tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;//更新路过的所有点

区间查询:如上面初始化数据的图,如果我们查询1~3范围的数据累加和,先从根节点开始查询,发现根节点的范围(1~4)比所要查找的范围大,发现其左右孩子的区间都与所要查找的区间范围有交集,所以开始查询其左右孩子的区间范围,左孩子的范围(1~2)被完全包含在所要查找的范围内,所以直接返回这个区间的值3,右孩子的范围(3~4),发现其左孩子的范围与所要查找的区间有交集,所以查询左孩子,并返回3,最后得到结果3+3=6。

我们总结一下,线段树的查询方法:

  1. 如果这个区间被完全包括在目标区间里面,直接返回这个区间的值

  2. 如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

  3. 如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

int ans = 0;
void query(int p, int l, int r)
{
    if (tree[p].l >= l && tree[p].r <= r)
    {
        ans+= tree[p].sum;
        return;
    }

    if (tree[p << 1].r >= l)//左孩子的右边界如果大于目标区间的左边界,代表有交集
        query(p << 1, l, r);
    if (tree[p << 1 | 1].l <= r)//右孩子的左边界如果小于目标区间的有边界,代表有交集
        query(p << 1 | 1, l, r);
}

区间修改,区间查询

区间修改的时候,我们按照如下原则:

1、如果当前区间被完全覆盖在目标区间里,讲这个区间的sum+k*(tree[i].r-tree[i].l+1),并对其进行懒标记

2、如果没有完全覆盖,则先下传懒标记

3、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

4、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

如下图所示,对1~3范围数据进行+2操作

void down(int p)
{
    if (tree[p].lz)//存在懒标记
    {

        //懒标记向下传递给左右孩子
        tree[p << 1].lz += tree[p].lz;
        tree[p << 1|1].lz += tree[p].lz;
        int mid = tree[p].l + tree[p].r >> 1;
        tree[p << 1].sum += tree[p].lz * (mid - tree[p << 1].l + 1);
        tree[p << 1 | 1].sum += tree[p].lz * (tree[p << 1 | 1].r - mid);
        tree[p].lz = 0;
    }
}
//区间修改
void add(int p, int l, int r, int k)
{
    if (tree[p].l >= l && tree[p].r <= r)
    {
        tree[p].sum += k * (tree[p].r - tree[p].l + 1);//区间有几个数据,就加几个k
        tree[p].lz = k;
        return;
    }
    down(p);//懒标记向下传递

    if (tree[p << 1].r >= l)
        add(p << 1, l, r, k);
    if (tree[p << 1 | 1].l <= r)
        add(p << 1 | 1, l, r, k);
    tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;//更新范围累加和
}

区间查询时也要结合懒机制

上面我们已经进行了1~3范围的+2操作,此时如果我们查询范围2~4。

根节点1~4,左右孩子都与目标区间有交集,查询到1~2范围时,发现这个范围有懒标记lz=2,与目标区间有交集2,此时我们需要把这个懒标记向下传递才能得到2的正确值。而3~4范围直接包含在目标范围内,所以直接返回其sum。

int ans = 0;
//范围查询
void query(int p, int l, int r)
{
    if (tree[p].l >= l && tree[p].r <= r)
    {
        ans += tree[p].sum;
        return;
    }
    down(p);//没有完全包含范围,如果存在懒标记需要向下传递
    if (tree[p << 1].r >= l)
        query(p << 1, l, r);
    if (tree[p << 1 | 1].l <= r)
        query(p << 1 | 1, l, r);
}

 这里注意如果修改操作是单点修改,那么懒机制也就不需要建立,每次修改直接一走到底就行。

相关文章:

  • 课题推荐——通信信号处理中的非线性系统状态估计(如信号跟踪、相位恢复等场景),使用无迹卡尔曼滤波(UKF)的非线性滤波算法,MATLAB实现
  • 【C++重载操作符与转换】输入和输出操作符
  • 深入解析Session与Cookie:从HTTP无状态到现代会话管理
  • 【kafka系列】消费者组
  • 使用Nexus搭建远程maven仓库
  • MySQL零基础入门:Ubuntu环境安装与操作精解
  • AWK 文本分析工具核心总结
  • HashMap,高效 哈希
  • Python生活手册-文件二进制:从快递柜到生鲜冷链的数据保鲜术
  • 业务流程BPM能力框架体系及华为中兴流程变革案例P83(83页PPT)(文末有下载方式)
  • python拜占庭将军
  • 【大模型实战篇】华为信创环境采用vllm部署QwQ-32B模型
  • 部署.NET6.0 Web API项目到Docker
  • 基于开源AI智能名片链动2+1模式S2B2C商城小程序的电商直播流量转化路径研究
  • 【Linux】Makefile
  • AI大模型基础设施:主流的几款开源AI大语言模型的本地部署成本
  • kafka学习笔记(四、生产者(客户端)深入研究(二)——消费者协调器与_consumer_offsets剖析)
  • windows系统搭建自己的ftp服务器,保姆级教程(用户验证+无验证)
  • 简单的Qwen3的本地部署、分析与常见报错
  • CSS学习笔记14——移动端相关知识(rem,媒体查询,less)
  • 旭辉控股集团:去年收入477.89亿元,长远计划逐步向轻资产业务模式转型
  • 宿州市委副书记任东已任市政府党组书记
  • 上海:以税务支持鼓励探索更多的创新,助力企业出海
  • 网商银行2024年年报发布,客户资产管理规模超过1万亿
  • 水利部将联合最高检开展黄河流域水生态保护专项行动
  • 暗蓝评《性别打结》丨拆解性别之结需要几步?