18-《进阶》线段树

1 线段树(又名为线段修改树)

线段树所要解决的问题是,区间的修改,查询和更新,如何更新查询的更快?

线段树结构提供三个主要的方法, 假设大小为N的数组,以下三个方法,均要达到O(logN) :

// L到R范围的数,每个数加上V
void add(int L, int R, int V, int[] arr);

// L到R范围的数,每个数都更新成V
void update(int L, int R, int V, int[] arr);

// L到R范围的数,累加和返回
int getSum(int L, int R, int[] arr);

1.1 线段树概念建立

1.1.1 累加和数组建立

1、对于大小为n的数组,我们二分它,每次二分我们都记录一个信息

2、对于每次二分,成立树结构,我们想拿任何区间的信息,可以由我们的二分结构组合得到。例如我们1到8的数组,可以二分得到的信息为:

graph TD
'1-8'-->'1-4'
'1-8'-->'5-8'
'1-4'-->'1-2'
'1-4'-->'3-4'
'5-8'-->'5-6'
'5-8'-->'7-8'
'1-2'-->'1'
'1-2'-->'2'
'3-4'-->'3'
'3-4'-->'4'
'5-6'-->'5'
'5-6'-->'6'
'7-8'-->'7'
'7-8'-->'8'

每一个节点的信息,可以由该节点左右孩子信息得到,最下层信息就是自己的信息。由以上的规则,对于N个数,我们需要申请2N-1个空间用来保存节点信息。如果N并非等于2的某次方,我们把N补成2的某次方的长度,用来保证我们构建出来的信息数是满二叉树。例如我们的长度是6,我们补到8个,后两个位置值为0。

对于任意的N,我们需要准备多少空间,可以把N补成2的某次方,得到的二分信息都装下?答案是4N。4N虽然有可能多分空间,但是多余的空间都是0,并无影响,而且兼容N为任意值的情况

例如四个数长度的数组arr[4]{3,2,5,7},我们得到累加和的二分信息为如下的树:

graph TD
'1到4=17'-->'1到2=5'
'1到4=17'-->'3到4=12'
'1到2=5'-->'3'
'1到2=5'-->'2'
'3到4=12'-->'5'
'3到4=12'-->'7'

我们申请4N的空间,即16,arr[16]。0位置不用。arr[1]=17,arr[2]=5,arr[3]=12,arr[4]=3,arr[5]=2,arr[6]=5,arr[7]=7。剩下位置都为0。任何一个节点左孩子下标为2i,右孩子下标为2i+1

得到累加和信息的分布树的大小,和值的情况,那么update更新树,和add累加树,同样的大小和同样的坐标关系构建。

1.1.2更新结构数组建立

懒更新概念,例如有8个数,我们要把1到6的数都减小2。那么先看1到6是否完全囊括8个数,如果囊括直接更新。很显然这里没有囊括,记录要更新1到6,下发该任务给1到4和5到8。1到6完全囊括1到4,记录到lazy中,不再下发;5到8没有囊括1到6,继续下发给5到6和7到8,5到6被囊括,记录到lazy不再继续下发,7到8不接受该任务

这种懒更新机制的时间复杂度为O(logN),由于一个区间经过左右子树下发,只会经过一个绝对路径到叶子节点,其他节点都会被懒住。如果某个节点有新的任务进来,会把之前懒住的信息下发给左右孩子

对于update操作,如果update操作经过的信息节点上存在懒任务,那么该次update操作会取消该节点的lazy,无需下发,因为下发了也会给update覆盖掉;

public class Code01_SegmentTree {

	public static class SegmentTree {
		// arr[]为原序列的信息从0开始,但在arr里是从1开始的
		// sum[]模拟线段树维护区间和
		// lazy[]为累加懒惰标记
		// change[]为更新的值
		// update[]为更新慵懒标记
		private int MAXN;
		private int[] arr;
                // 4*arr.length()
		private int[] sum;
                // 4*arr.length()
		private int[] lazy;
                // 4*arr.length()
		private int[] change;
                // 4*arr.length()
		private boolean[] update;

                // 根据int[] origin来初始化我们的线段树结构
		public SegmentTree(int[] origin) {
			MAXN = origin.length + 1;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值