线段树GO实现

var sum []int
var lazyT[]int
var arr[]int
//改变了区间中某一个值或某些值时,我们需要沿着线段树向上更新改变值的节点与根节点间的所有值,可以看出这部操作与线段树的高度有关,复杂度为O(logn)
func pushUp(idx int){
	sum[idx]=sum[idx*2]+sum[idx*2+1]
}

//build
func build(left,right,idx int){
	if left==right{
		//单节点
		sum[idx]=arr[left]
		return
	}
	var mid=left+(right-left)/2
	build(left,mid,idx*2)
	build(mid+1,right,idx*2+1)

	pushUp(idx)
}

//单点修改。改变区件中的某一个值,即在树中找到对应的叶节点,更新其值,然后递归改变该节点到根节点路径上所有节点的值
func updatePoint(changedIdx,changedValue,left,right,idx int){
	if left==right{
		//单节点
		sum[idx]+=changedValue
		return
	}
	var mid=left+(right-left)/2
	if changedIdx<=mid{
		updatePoint(changedIdx,changedValue,left,mid,idx*2)
	}else{
		updatePoint(changedValue,changedValue,mid+1,right,idx*2+1)
	}
	pushUp(idx)//子节点更新了,所以本节点也需要更新信息
}

//区间修改,在区间修改的时候我们引入一个新的概念——懒惰标记
//
//懒惰标记:表示本节点的统计信息已经根据标记更新过了,但是本节点的子节点还没有更新
//
func pushDown(idx,leftNum,rightTree int){
	//leftNum,rightTree 左子树,右子树的数字数量
	//懒惰标记下推
	if lazyT[idx]>0{
		lazyT[idx*2]+=lazyT[idx]
		lazyT[idx+2+1]+=lazyT[idx]
		sum[idx*2]+=lazyT[idx]*leftNum
		sum[idx*2+1]+=lazyT[idx]*rightTree
		lazyT[idx]=0//清除本节点标记
	}
}
func updateSeg(changedLeftIdx,changedRightIdx, changedValue, left, right,idx int)  {
	if changedLeftIdx<=left&&right<=changedRightIdx{
		//如果本区间完全在操作区间[L,R]以内
		sum[idx]+=changedValue*(right-left+1)
		lazyT[idx]+=changedValue增加lazyT标记,表示本区间的Sum正确,子区间的Sum仍需要根据lazyT的值来调整
	}
	var mid=left+(right-left)/2
	pushDown(idx,mid-left+1,right-mid)
	if changedLeftIdx<=mid{
		updateSeg(changedLeftIdx,changedRightIdx,changedValue,left,mid,idx*2)
	}
	if changedRightIdx>mid{
		updateSeg(changedLeftIdx,changedRightIdx,changedValue,mid+1,right,idx*2+1)
	}
	pushUp(idx)//更新本节点信息
}

//查询,查询过程中有一个很重要的操作便是懒惰标记的下推,理解了懒惰标记的下推,查询操作就很好理解
func query(queryLeft,queryRight, left, right, idx int)int{
	if queryLeft<=left&&right<=queryRight{
		//在区间[L,R]以内
		return sum[idx]
	}
	var mid=left+(right-left)/2
	pushDown(idx,mid-left+1,right-mid)
	var res int
	if queryLeft<=mid{
		res+=query(queryLeft,queryRight,left,mid,idx*2)
	}
	if queryRight>mid{
		res+=query(queryLeft,queryRight,mid+1,right,idx*2+1)
	}
	return res
}

参考:12

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值