什么是线段树?

news2024/11/24 0:29:47

线段树是用于储存区间信息的数据结构。

线段树将区间划分为左右子区间进行递归求解,便形成了树形结构。并通过合并两区间信息从而取得任意区间信息

例如对于数组a={10, 11, 12, 13, 14},那么就可以构建以下线段树

在这里插入图片描述

构建

以数组作为线段树的基本结构,储存信息

type SegmentTree struct {
	// 原数组
	arr []int
	// 线段树
	st []int
	// 懒惰标记。延迟对节点修改,在访问时才进行修改
	lazyMark []int
}

func NewSegmentTree(arr []int) *SegmentTree {
	if len(arr) == 0 {
		return nil
	}

	segTree := SegmentTree{
		arr:      arr,
		st:       make([]int, 4*len(arr)),
		lazyMark: make([]int, 4*len(arr)),
	}

	for i := range segTree.lazyMark {
		//fill LazyTree with empty lazy nodes
		segTree.lazyMark[i] = emptyLazyNode
	}

	//starts with node 1 and interval [0, len(arr)-1] inclusive
	segTree.Build(1, 0, len(arr)-1)

	return &segTree
}

func (s *SegmentTree) Build(node int, left, right int) {
	// 当左右边界相等时,线段节点就是数组节点
	if left == right {
		s.st[node] = s.arr[left]
		return
	}

	// 将数组分为两部分进行构建
	mid := (left + right) / 2

	s.Build(2*node, left, mid)
	s.Build(2*node, mid+1, right)

	s.st[node] = s.st[2*node] + s.st[2*node+1]
}

区间查询

区间查询是通过组合多个子区间合并信息来实现的

// left、right指向节点区间
// ql、qr指向查询区间
func (s *SegmentTree) Query(node int, left, right int, ql, qr int) int {
	// 无效值直接返回
	if (ql > qr) || (left > right) {
		return 0
	}

	// 若存在标记,则更新
	s.Propagate(node, left, right)

	// 当前区间为询问区间的子集时直接返回当前区间的和
	if left >= ql && right <= qr {
		return s.st[node]
	}

	// 继续分左右子区间递归查询
	mid := (left + right) / 2
	leftNodeSum := s.Query(2*node, left, mid, ql, minInt(mid, qr))
	rightNodeSum := s.Query(2*node+1, mid+1, right, maxInt(ql, mid+1), qr)

	return leftNodeSum + rightNodeSum
}

func minInt(x, y int) int {
	if x < y {
		return x
	}
	return y
}

func maxInt(x, y int) int {
	if x > y {
		return x
	}
	return y
}

区间修改与懒惰标记

在前文中已经出现了Propagate方法,那么这个方法是做什么的呢?

考虑到如果修改区间会导致包含在该区间内的所有节点都遍历一次,修改一次,那么这会导致时间复杂度非常高,因此引入了懒惰标记。而Propagate方法则是在查询和更新相应节点时更新懒惰标记

懒惰标记,简单来说,就是通过延迟对节点信息的更改,从而减少可能不必要的操作次数。每次执行修改时,我们通过打标记的方法表明该节点对应的区间在某一次操作中被更改,但不更新该节点的子节点的信息。

现在假如说准备给区间[3,5]中每个数都加上3,那么根据区间查询规则,匹配到了[3,3]和[4,5]两个区间,因此直接更新这两个区间的懒惰标记lazyMark=3。

接着查询了[4,4]区间的数字,触发懒惰更新,使得[4,5]信息更新,并将标记下方到其子区间

func (s *SegmentTree) Update(node int, left, right int, ql, qr int, val int) {
	// 触发懒惰标记更新
	s.Propagate(node, left, right)

	// 无效值返回
	if ql > qr || left > right {
		return
	}

	if left >= ql && right <= qr {
		// 当前区间为查询区间的子区间,则更新懒惰标记,并传递更新
		s.lazyMark[node] += val
		s.Propagate(node, left, right)
	} else {
		// 分左右子区间递归更新
		mid := (left + right) / 2

		s.Update(2*node, left, mid, ql, minInt(mid, qr), val)
		s.Update(2*node+1, mid+1, right, maxInt(ql, mid+1), qr, val)

		s.st[node] = s.st[2*node] + s.st[2*node+1]
	}
}

func (s *SegmentTree) Propagate(node, left, right int) {
	// 如果存在标记
	if s.lazyMark[node] != emptyLazyNode {
		s.st[node] += (right - left + 1) * s.lazyMark[node]

		if left == right {
			// 被标记的是叶节点,那么直接更新
			s.arr[left] += s.lazyMark[node]
		} else {
			// 其他情况下,更新左右子线段节点的标记
			s.lazyMark[2*node] += s.lazyMark[node]
			s.lazyMark[2*node+1] += s.lazyMark[node]
		}

		s.lazyMark[node] = emptyLazyNode
	}
}

Ref

  1. https://oi-wiki.org/ds/seg/
  2. https://github.com/dougwatson/Go/blob/master/structure/segmenttree/segmenttree.go

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1406067.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

残差连接是什么意思

残差连接是深度神经网络中一种用于缓解梯度消失问题的技术。它的核心思想是通过将网络的输入直接传递到网络的输出&#xff0c;从而构建了一条直达路径&#xff0c;使得梯度更容易通过整个网络传播。这有助于在训练深层网络时避免梯度消失或梯度爆炸的问题。 在残差连接中&…

linux|操作系统|centos7物理机安装网卡驱动8188gu(内核升级,firmware固件,USB设备管理,module管理)

前言&#xff1a; 目前服务器领域centos7基本是主流的操作系统&#xff0c;而linux相对于Windows来说&#xff0c;软硬件方面的支持是差很多的&#xff0c;在硬件方面来说&#xff0c;以一个免驱的网卡为例&#xff0c;window xp可能不会自动识别到&#xff0c;但Windows10基本…

数据库中的经纬度数据如何在QGIS中显示

思路&#xff1a;必须先将经纬度数据转换成POINT&#xff0c;MULTILINESTRING等格式才能在QGIS中展示 步骤 1、首先在postgresql数据中建一张包括经纬度数据的表 **注意&#xff1a;**如果是新建数据库&#xff0c;一定要执行如下代码&#xff0c;否则后面的函数ST_GeomFrom…

使用fastapi和apifox实现后端接口

使用python文件import fastapi和uvicorn编写接口脚本 格式例&#xff1a; from pydantic import BaseModel from fastapi import FastAPI import uvicorn import jsonappFastAPI()class Response_data(BaseModel):re: strclass YourService():def __init__(self):passdef f(s…

【centos7安装docker】

背景&#xff1a; 学习docker&#xff0c;我是想做一个隔离环境&#xff0c;并且部署的话&#xff0c;希望实现自动化&#xff0c;不为安装软件而烦恼&#xff0c;保证每个人的环境一致。 2C4G内存 50G磁盘的虚拟机事先已经准备完毕。 1.查看下centos版本&#xff0c;docker要…

pytest自动化测试框架—基础篇

Pytest是一种基于Python编程语言的自动化测试框架&#xff0c;它提供了丰富的功能和灵活的扩展性&#xff0c;可以用于单元测试、集成测试、功能测试、端到端测试等多种场景。本文将介绍Pytest框架的基础知识&#xff0c;包括安装、配置、运行测试、断言和参数化等方面。 一、安…

shell脚本概念与命令

一、shell的作用 Shell 是一个特殊的应用程序&#xff0c;它介于操作系统内核与用户之间&#xff0c;充当 了一个“命令解释器”的角色&#xff0c;负责接收用户输入的操作指令&#xff08;命令&#xff09;并进行解释&#xff0c;将需要执 行的操作传递给内核执行&#xff0c;…

二分法——C++

二分分为整数二分和浮点数二分&#xff0c;其中比较复杂的是整数二分&#xff0c;简单一点的是浮点数二分。 我们首先来说明整数二分,主要来讲解模板。 整数二分&#xff1a; 我们先来说一说使用二分法的前提&#xff0c;要有单调性&#xff0c;然后可以根据某种性质来划分成…

【算法小记】——机器学习中的概率论和线性代数,附线性回归matlab例程

内容包含笔者个人理解&#xff0c;如果错误欢迎评论私信告诉我 线性回归matlab部分参考了up主DR_CAN博士的课程 机器学习与概率论 在回归拟合数据时&#xff0c;根据拟合对象&#xff0c;可以把分类问题视为一种简答的逻辑回归。在逻辑回归中算法不去拟合一段数据而是判断输入…

linux杀毒软件clamav安装使用

1、下载 在下面地址下载&#xff1a;https://www.clamav.net/downloads 2、安装 clamav-1.2.1.linux.x86_64.rpm放在/home路径。 执行&#xff1a; chmod -R 777 /home/clamav-1.2.1.linux.x86_64.rpm rpm -ivh clamav-1.2.1.linux.x86_64.rpm3、下载病毒库 下载路径&am…

数仓建设学习路线(三)元数据管理

什么是元数据&#xff1f; 简单来说就是描述数据的数据&#xff0c;更直白来说就是描述表名、表制作者、表字段、表生命周期、表存粗等信息的数据 元数据该如何管理 工具化 开源&#xff1a; 可通过atlas获取表依赖及信息做二次开发&#xff0c;或者完成可视化界面 平台化&am…

梳理Langchain-Chatchat知识库API接口

一.Langchain-Chatchat 知识库管理 1.Langchain-Chatchat 对话和知识库管理界面 Langchain-Chatchat v0.28 完整的界面截图&#xff0c;如下所示&#xff1a; 2.知识库中源文件和向量库 知识库 test 中源文件和向量库的位置&#xff0c;如下所示&#xff1a; 3.知识库表结构 k…

JavaWeb之开发介绍 --黑马笔记

什么是 Web &#xff1f; Web&#xff1a;全球广域网&#xff0c;也称为万维网(www World Wide Web)&#xff0c;能够通过浏览器访问的网站。 Web 网站的工作流程 上图解释&#xff1a; 当你在浏览器中输入网址或点击一个链接时&#xff0c;浏览器会向前端服务器发起请求&…

CSAPP fall2015 深入理解计算机系统 Cache lab详解

Cache Lab cache lab 缓存实验 代码下载 从CSAPP上面下载对应的lab代码 http://csapp.cs.cmu.edu/3e/labs.html 环境准备 需要安装 valgrind。可以参考文章Valgrind centos。 安装好以后执行valgrind --version可以看到版本号。 Cache simulator cache simulator not a …

API接口安全总结

接口分类 HTTP接口 RPC接口&#xff08;客户端和服务器端的连接 例如游戏登陆&#xff09;非web协议&#xff0c;PRC 远程过程调用 Remote Procedure Call&#xff0c;其就是一个节点请求另外一个节点提供的服务。当两个物理分离的子系统需要建立逻辑上的关联时&#xff0c;R…

第08章_面向对象编程(高级)(static,单例设计模式,理解mian方法,代码块,final,抽象类与抽象方法,接口,内部类,枚举类,注解,包装类)

文章目录 第08章_面向对象编程(高级)本章专题与脉络1. 关键字&#xff1a;static1.1 类属性、类方法的设计思想1.2 static关键字1.3 静态变量1.3.1 语法格式1.3.2 静态变量的特点1.3.3 举例1.3.4 内存解析 1.4 静态方法1.4.1 语法格式1.4.2 静态方法的特点1.4.3 举例 1.5 练习 …

小土堆pytorch学习笔记001

1、Pytorch环境的配置与安装。 &#xff08;1&#xff09;建议安装&#xff1a;Anaconda &#xff08;2&#xff09;检查显卡&#xff1a;GPU &#xff08;3&#xff09;管理环境&#xff08;不同版本的pytorch 版本不同&#xff09;&#xff1a; conda create -n pytorch…

【开源】基于JAVA+Vue+SpringBoot的农家乐订餐系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户2.2 管理员 三、系统展示四、核心代码4.1 查询菜品类型4.2 查询菜品4.3 加购菜品4.4 新增菜品收藏4.5 新增菜品留言 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的农家乐订餐系统&#xff0c…

防御保护----信息安全

网络安全概述 信息安全&#xff1a;防止任何对数据进行未授权访问的措施&#xff0c;或者防止造成信息有意无意泄露、破坏、丢失等问题的发生&#xff0c;让数据处于远离危险、免于威胁的状态和特性。 网络安全&#xff1a;计算机网络环境下的信息安全。 网络安全背景 网络空间…

STM32单片机学习5--STM32中断

文章目录 一、前言二、NVIC中断控制器2.1、NVIC结构体成员2.2、抢占优先级和响应优先级2.3、NVIC的优先级组 三、EXTI外部中断四、中断实战4.1、确定连线4.2、配置中断控制端口4.3、配置中断端口4.4、配置中断服务函数4.5、主函数调用 一、前言 单片机无系统执行逻辑&#xff…