• 区间信息维护与查询【线段树 】 - 原理1 线段树的基本操作


    区间信息维护与查询【线段树 】 - 原理1 线段树的基本操作

    线段树(segment tree)是一种基于分治思想的二叉树,它的每个节点都对应一个[L , R ]区间,叶子节点对应的区间L =R 。每一个非叶子节点[L , R ]其左子节点的区间都为[L , (L +R )/2],右子节点的区间都为[(L +R )/2+1, R ]。

    [1, 10]区间的线段树如下图所示:

    在这里插入图片描述

    因为线段树对区间进行二分,是一棵平衡二叉树,所以树高为O(logn ),树上的操作大多与树高相关。

    线段树主要用于更新和查询,一般至少有一个是区间更新或查询。更新和查询的种类变化多样,灵活运用线段树可以解决多种问题。

    【1】 线段树的存储方式

    对于区间最值(最大值或最小值)查询问题,线段树的每个节点都包含三个域:l、r、 mx,其中l 和r 分别表示区间的左右端点,mx表示[l , r ]区间的最值。

    本题以最大值为例,若有10个元素a [1…10]={5, 3, 7, 2, 12, 1, 6, 4, 8, 15},则构建的线段树如下图所示。

    在这里插入图片描述

    线段树除了最后一层,其他层构成一颗满二叉树,因此采用顺序存储方式,用一个数组tree[]存储节点。

    若一个节点的存储下标为k ,则其左子节点的下标为2k ,其右子节点的下标为2k +1。

    在这里插入图片描述

    线段树根节点的存储下标为1,其左右子节点的存储下标分别为2、3,有10个元素的线段树,其存储下标如下图所示:

    在这里插入图片描述

    【2】创建线段树

    可以采用递归的方法创建线段树,算法步骤如下:

    ① 若是叶子节点(l =r ),则节点的最值就是对应位置的元素值。

    ② 若是非叶子节点,则递归创建左子树和右子树。

    ③ 节点的区间最值等于该节点左右子树最值的最大值。

    [ 算法代码]

    void build(int k , int l , int r){ //创建线段树,节点的存储下标为k,节点的区间为 [l , r]
    	tree[k].l = l;
    	tree[k].r = r;
    	
    	if(l == r){
    		tree[k].mx = a[l];
    		return;
    	}
    	
    	int mid , lc , rc;
    	mid = (l + r) / 2; //划分点
    	lc = k * 2; // 左子节点的存储下标
    	rc = k * 2 + 1; //右子节点的 存储下标
    	build(lc , l , mid);
    	build(rc , mid + 1, r);
    	
    	tree[k].mx = max(tree[lc].mx , tree[rc].mx); //节点的最大值等于左右子节点 最值的最大值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    【3】点更新

    点更新指修改一个元素的值,例如将a [i ]修改为v 。采用递归进行点更新,算法步骤如下。

    ① 若是叶子节点,满足l =r 且l =i ,则修改该节点的最值为v。

    ② 若是非叶子节点,则判断是在左子树中更新还是在右子树中更新。

    ③ 返回时更新节点的最值。

    例如,修改第5个节点的值为14时,先从树根向下找第5个元素所在的叶子节点,将其最值修改为14,返回时更新路径上所有节点的最值(左右子节点最值的最大值)。

    在这里插入图片描述

    [ 算法代码]

    void update(int k , int i , int v){ // 将a[i] 更新为 v
    	
    	if(tree[k].l == tree[k].r && tree[k].l == i){ //找到a[i]
    		
    		tree[k].mx = v;
    		return ;
    	}
    	
    	int mid , lc, rc;
    	mid = (tree[k].l + tree[k].r) / 2; //划分点
    	
    	lc = k * 2; // 左子节点的存储下标
    	rc = k * 2 + 1; // 右子节点的 存储下标
    	
    	if(i <= mid){
    		update(lc , i ,v); // 到左子树中 更新
    	}else{
    		update(rc , i , v); //到 右子树中更新
    	}
    	
    	tree[k].mx = max(tree[lc].mx , tree[rc].mx); // 返回时更新最值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    【4】区间查询

    区间查询指查询一个[l , r ]区间的最值。采用递归的方法进行区间查询的算法步骤如下:

    ① 若节点所在的区间被查询区间[l , r ]覆盖,则返回该节点的最值。

    ② 判断是在左子树中查询,还是在右子树中查询。

    ③ 返回最值。

    例如,在[1, 10]的线段树中查询[3, 5]区间的最值,过程如下。

    ① 计算树根[1, 10]的划分点,mid=(1+10)/2=5,待查询区间l=3、r =5、r ≤mid,说明查询区间在左子树中,到左子树中查询

    在这里插入图片描述

    ② 计算左子树树根[1, 5]的划分点,mid=(1+5)/2=3,待查询区间l =3、r =5、r >mid、l ≤mid,说明查询区间横跨左右子树两个区间,需要到左、右子树中查询[3, 5],然后求最大值。

    在这里插入图片描述

    ③ 计算左子树树根[1, 3]的划分点,mid=(1+3)/2=2,待查询区间l =3、r =5、l >mid,说明查询区间在右子树中,到右子树中查询,该右子树[3, 3]被查询区间覆盖,返回最值7。

    在这里插入图片描述

    ④ 右子树为[4, 5],待查询区间为[3, 5],被查询区间覆盖,返回区间最值12。

    在这里插入图片描述

    ⑤ 左右子树分别返回最值7、12,然后求最大值,即可得到查询区间[3, 5]的最值为12。

    在这里插入图片描述

    [ 算法代码]

    int query(int k , int l,  int r){ // 求[l ,r] 区间的最值
    	
    	if(tree[k].l >= l && tree[k].r <= r){ // 查询区间覆盖该节点区间
    		return tree[k].mx;	
    	}
    	
    	int mid , lc , rc;
    	mid = (tree[k].l + tree[k].r ) / 2; //划分点
    	
    	lc = k * 2; //左子节点的存储下标
    	rc = k * 2 + 1; //右子节点的 存储下标
    	
    	int Max = -inf; //注意: 局部变量可以,全局变量不可以
    	if(l <= mid){
    		Max = max(Max , query(lc , l , r)); // 到左子树中查询
    	}
    	if(r > mid){
    		Max = max(Max , query(rc , l , r)); //到右子树中查询
    	}
    	
    	return Max;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    引入依赖时,右键能点击进入,运行时报错
    LLC 三相移相PWM产生原理分析
    【RocketMQ】深入剖析延迟消息核心实现原理
    bundle文件压缩库的使用
    Redis持久化实战
    缓存同步之 RabbitMQ 和 Canal
    国企云计算厂商增长迅猛,但私企云下滑
    c++:数据结构链表list的模拟实现
    开源.NetCore通用工具库Xmtool使用连载 - 图形验证码篇
    274. H 指数
  • 原文地址:https://blog.csdn.net/weixin_44226181/article/details/128091650