Go语言实现动态开点线段树详解

 更新时间:2025年02月26日 08:15:41   作者:MelonTe  
线段树是一种用于高效处理区间查询和区间更新的数据结构,下面我们就来看看如何使用Go实现动态开点线段树的方式,感兴趣的可以了解下

1、线段树介绍

线段树是一种用于高效处理区间查询和区间更新的数据结构,当我们需要解决一个频繁更新区间值的问题的时候,就可以采用线段树的结构进行解决。线段树的核心思想是将区间分为多个子区间进行管理,越往下区间范围越小,根节点表示整个线段树能表示的区间。

本文记录使用Go实现动态开点线段树的方式,该模板的线段树用于解决区间求和问题,还有求解区间最小值、最大值的线段树可以进行微调修改即可。

区间查询、区间更新的时间复杂度均为O(logN)。

2、动态开点线段树实现

动态开点的核心在于,需要缩小范围,即进入子节点的时候再进行创建,相对于使用数组来实现线段树,可以更大的减小空间开销。

线段树节点

一个节点需要记录它的左子节点、右子节点、当前节点表示的区间的和val,以及暂未下推给子节点的懒惰值lazy。

type SegTreeNode struct {
    lazy  int
    val   int
    left  *SegTreeNode
    right *SegTreeNode
}

线段树的创建

整个线段树只需要记录一个根节点以及该线段树表示的区间上届。

type SegTree struct {
    //线段树的范围,0~N
    N    int
    root *SegTreeNode
}
 
// 创建线段树
func CreateSegTree(n int) *SegTree {
    return &SegTree{
        N: n,
        root: &SegTreeNode{
            lazy:  0,
            val:   0,
            left:  nil,
            right: nil,
        },
    }
}

递归上推

当更新完了子节点后,回到当前节点的时候,需要更新当前节点的值,表示从树的底部上推值。

// 递归上推
func (ST *SegTree) Pushup(node *SegTreeNode) {
    node.val = node.left.val + node.right.val
}

懒惰下推

当需要缩小查找区间的时候,需要向下查找,这时候要先把懒惰值下推,防止查找出错误的结果,也防止子节点还未创建。

// 同步下推
func (ST *SegTree) Pushdown(node *SegTreeNode, leftnum, rightnum int) {
    //创建左右节点
    if node.left == nil {
        node.left = new(SegTreeNode)
    }
    if node.right == nil {
        node.right = new(SegTreeNode)
    }
    //下推节点懒惰标记
    if node.lazy == 0 {
        return
    }
    node.left.val += leftnum * node.lazy
    node.right.val += rightnum * node.lazy
    //下推
    node.left.lazy += node.lazy
    node.right.lazy += node.lazy
    //置零
    node.lazy = 0
}

首先先创建左右节点,如果没有需要下推的懒惰标记则直接返回。否则就更新左右节点的val和lazy。

更新操作

// 更新操作,更新[left,right]区间的值,start和end是当前处在区间
func (ST *SegTree) Update(node *SegTreeNode, start, end, left, right, val int) {
    if left <= start && end <= right {
        //锁定区间,进行更新
        node.val += (end - start + 1) * val
        node.lazy += val
        return
    }
    //缩小区间
    mid := (start + end) / 2
    //需要找到子节点,先下推懒惰标记
    ST.Pushdown(node, mid-start+1, end-mid)
    if mid >= left {
        ST.Update(node.left, start, mid, left, right, val)
    }
    if mid+1 <= right {
        ST.Update(node.right, mid+1, end, left, right, val)
    }
    //递归
    ST.Pushup(node)
}

left和right表示要更新的区间,而start和end表示当前区间。如果当前区间处在需要更新的区间内,则直接更新区间值以及懒惰值,然后直接返回即可,此时不需要继续更新下面节点的值,这是动态开点的关键所在。

若当前区间并未完全处在需要更新的区间内,则二分该区间,缩小范围进行更新。

例如在一次操作需要更新的是[30,40]范围的值,而当前区间处在[25,50]中,当前区间并未完全处在更新区间,则二分为[25,37]和[38,50],左区间和右区间均和需要更新的区间存在交集,那么就往下更新,直到更新区间包含当前区间。

在更新完后,进行一次上推。

查询操作

与更新操作类似,只需要一个ans来记录答案并且返回。

// 查询操作,返回区间的值
func (ST *SegTree) Query(node *SegTreeNode, start, end, left, right int) int {
    if left <= start && end <= right {
        return node.val
    }
    mid := (start + end) / 2
    ST.Pushdown(node, mid-start+1, end-mid)
    ans := 0
    if left <= mid {
        ans += ST.Query(node.left, start, mid, left, right)
    }
    if mid+1 <= right {
        ans += ST.Query(node.right, mid+1, end, left, right)
    }
    return ans
}

到此这篇关于Go语言实现动态开点线段树详解的文章就介绍到这了,更多相关Go线段树内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • go-cqhttp环境配置及安装过程

    go-cqhttp环境配置及安装过程

    这篇文章主要介绍了go-cqhttp环境配置,包括go-cqhttp安装及简单介绍,本文结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • Go语言中三个输入函数(scanf,scan,scanln)的区别解析

    Go语言中三个输入函数(scanf,scan,scanln)的区别解析

    本文详细介绍了Go语言中三个输入函数Scanf、Scan和Scanln的区别,包括用法、功能和输入终止条件等,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-10-10
  • Go简单实现协程方法

    Go简单实现协程方法

    本文主要介绍了Go简单实现协程的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-12-12
  • go语言切片去重的3种方式

    go语言切片去重的3种方式

    go语言中的切片是使用非常频繁的一个数据结构,本文主要介绍了go语言切片去重的3种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • Go语言异常处理error、panic、recover的使用

    Go语言异常处理error、panic、recover的使用

    GO语言中引入的异常的处理方式为error、panic、recover ,本文主要介绍了Go语言异常处理error、panic、recover的使用,感兴趣的可以了解一下
    2024-08-08
  • 浅谈Golang内存逃逸

    浅谈Golang内存逃逸

    本文主要介绍了Golang内存逃逸,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • go语言编程实现递归函数示例详解

    go语言编程实现递归函数示例详解

    这篇文章主要为大家介绍了go语言编程实现递归函数示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Golang Cron 定时任务的实现示例

    Golang Cron 定时任务的实现示例

    这篇文章主要介绍了Golang Cron 定时任务的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • go json编译原理XJSON实现四则运算

    go json编译原理XJSON实现四则运算

    这篇文章主要为大家介绍了go json编译原理XJSON实现四则运算示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • go语言面试如何实现自旋锁?

    go语言面试如何实现自旋锁?

    这篇文章主要为大家介绍了go语言面试中常问的如何实现自旋锁问题实例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11

最新评论