【算法】线段树详解

线段树是一种二叉搜索树,用于高效处理区间查询和更新操作。在传统的数组区间求和问题中,查询和更新的时间复杂度为O(n)。通过线段树,可以将这两个操作的时间复杂度降低到O(log(n))。线段树通过将大区间分解为小区间,递归计算得到答案。本文介绍了线段树的基本原理,构建过程,以及如何使用懒标签优化区间更新,避免冗余操作。线段树在区间运算、动态RMQ问题等方面有广泛应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.概述

在说线段树之前,我们先来了解一个问题

给你一串数组a,求一段区间[L,R]的和,该数组的值随时可以更新

传统的做法:
 每次查询某一区间的和,我们声明一个变量sum=0,然后令i从L枚举到R,依此加上a[i],但是这时候的查询时间复杂度为O(n)
原数组更新时,例如让第i个数改为5,我们只需要直接令a[i]=5就能完成更新,此时更新的时间复杂度为O(1)

前缀和做法:
 这时候很多人会想到直接声明一个数组sum,先预处理出前缀和,然后直接访问sum[R]-sum[L-1]就是所求值,此时一查询的时间复杂度为O(1)
 但是,如果原数组的数据更新呢?这时候直接用前缀和的知识来写,我们每更新一次数组,都要更新前缀和,时间复杂度达到O(n)

总结
无论是传统做法还是前缀和算法,查询或者更新总有一个时间复杂度达到O(n),当数据大点的时候,就容易超时。那么我们有没有一种算法,让查询和更新的时间复杂度都小于O(n)呢?这里就要介绍本次文章的主角——线段树

二.线段树

 线段树,是一个二叉搜索树,它的主要思想是将一个大的区间上的问题分解为左右两个小区间的问题求解,进而求解出大的区间上的答案。例如,我们想要求解区间长度为10上的问题,它构造出来的线段树如下图所示
在这里插入图片描述
 可以看出,每次线段树的深度最大不会超过⌊log⁡(n)+1\log(n)+1log(n)+1⌋,如此一来,其查询的时间复杂度为O(log⁡(n)\log(n)log(n)),其更新操作的时间复杂度也是O(log⁡(n)\log(n)log(n))

线段树的编写

假设有一串数组a,长度为[L, R],对于线段树的编写,有以下基本步骤

  1. 计算出区间的中点mid,将区间分为两个小区间[L, mid], [mid+1, R]
  2. 对两个区间递归分解,直到区间长度为1,得到这个区间的答案
  3. 依此回溯得到大区间的答案
  4. 最终得到[L, R]的答案

可能有的人看到这还是有点懵懂,接下来我们来根据一道例题来讲解

假设有一个数组a[]={1,2,3,4,5,6,7,8,9,10},我们要进行区间求和,接下来我们来构造线段树,先把区间分成这样一个形式
在这里插入图片描述
从下往上计算答案
在这里插入图片描述
计算第4层答案,有子元素的为子元素的和
在这里插入图片描述
第三层,第二层,第一层同理,最后得到下图
在这里插入图片描述
这样我们线段树的构造完成了!
那么,构造完线段树,我们都是怎么进行查询的呢?假设总的区间长度为[start, end],要查询的区间为[L, R]。
一般来说,查询的步骤如下

  1. 当前区间为[start, end]时,如果L<=start且R>=end,则返回当前区间的答案
  2. 否则计算出[start, end]的中点mid. 如果L<=mid,则向[start, mid]搜索答案,R>mid时向[mid+1, end]搜索答案
  3. 重复1,2步,直到将答案搜索到

例如,我们要求解[4,9]之间的区间和是多少时
1.因为[1,10]的中点为5,而4<5而9>5,所以我们向两边搜索答案,如下图,红色为判断过的,绿色为还未判断
在这里插入图片描述
在区间[1,5],中点mid=3,又因为L>mid, 但是R>mid,所以只向右孩子搜索
同理对区间[6,10],中点mid=8,因为L<mid且R>mid所以向两个孩子都搜索,如下图
在这里插入图片描述
对[4,5],因为L<=4, R>=5,所以左边区间的答案就是[4,5]的值,值为9,对[6,8]也同理,得到值21,对[9,10]继续处理,最后在[9]的到值9

在这里插入图片描述
最后res=39,而4+5+……+9刚好等于39,答案没错,事实证明这样处理,查询得到的结果为对的!
对于更新操作,和查询操作差不多,只不过最后将值改变之后在往上回溯的到更新后的值,这里我就不多赘述了!

三.代码

根据上面的思想我们可以开始写代码了
构建线段树,假设我们数据都在num数组里,我们在tree数组里进行构建线段树

void build_tree(int node,int start,int end)//node为当前结点,对[start,end]进行构建
{
   
   
	if(start==end) {
   
   
		tree[node]=num[start];
	} else {
   
   
		
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值