线段树模板(Java)

news2024/10/7 16:22:45

线段树

  • 一、线段树概念
  • 二、线段树模板
    • 1.建树
    • 2. 单点修改
    • 3.区间查询
    • 4.完整代码及测试

一、线段树概念

  线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。它的主要优势是对于区间求和、区间求最大值、区间修改和单点修改的速度快,时间复杂度能达到 O ( l o g N ) O(logN) O(logN)
  若以常规的方法在数组中进行区间求和等操作,时间复杂度会达到 O ( n ) O(n) O(n),若操作的次数量非常大,那么就很容易超时。线段树的优势就体现出来了
  线段树的实现基于一维数组,用数组下标 2 ∗ k + 1 2 * k +1 2k+1 的元素代表左儿子,用下标 2 ∗ k + 2 2 * k +2 2k+2 的元素代表右儿子来进行树的模拟

对于本文有不理解的小伙伴,建议看B站的这个视频:线段树

二、线段树模板

1.建树

  • 线段树建树的操作跟二叉树的建树操作很类似,都利用递归,构建左儿子和右儿子。
  • 任意一个结点 k k k,它的左儿子为第 2 ∗ k + 1 2 * k +1 2k+1 个元素,右儿子为第 2 ∗ k + 2 2 * k +2 2k+2 个元素。本例根结点存储的是左儿子和右儿子的和,可应用于区间求和的场景
  • 建树时,需要声明一个新的一维数组来存储树的元素,这个数组的大小一般设为原数组长度的4倍及以上
  • static int[] arr = {1,3,5,7,9,11};
    static int[] tree = new int[4 * arr.length];

代码:

	/**
	 * @param node 当前结点
	 * @param l 当前结点对应的区间为l~r
	 * @param r
	 */
	public static void build(int node, int l, int r) {
		if (l == r) {
			tree[node] = arr[l];
			return;
		}
		int mid = (l + r) >> 1;
		int l_child = 2 * node + 1;
		int r_child = 2 * node + 2;
		build(l_child, l, mid);	//构建左儿子
		build(r_child, mid + 1, r);	//构建右儿子
		//子树构建好后,更新父结点元素
		tree[node] = tree[l_child] + tree[r_child];
	}

下面画个图理解建立好的树
在这里插入图片描述

可以看出:

  • 叶节点存储原数组的元素,父节点存储左儿子和右儿子的区间和。(对于不同场景,父节点存储元素的意义不同,比如区间求最大值,父节点也可以左儿子和右儿子的区间最大值)
  • 线段树采用的是空间换时间,从建树后的tree数组可以看出,有很多空间都没有利用。

2. 单点修改

  • 判断修改的点在左子树的区间还是右子树,若在左子树,递归左子树,修改对应的点,反之递归右子树
  • 修改后,更新父节点的值

代码:

	/**
	 * @param node 	当前结点
	 * @param l		当前结点对应的区间为l~r
	 * @param r
	 * @param idx	需更新点的下标(原数组下标)
	 * @param val	更新为什么值
	 */
	public static void update(int node, int l, int r, int idx, int val) {
		if (l == r) {	//l=r的时候,表示找到了idx对应的结点
			tree[node] = val;	//更新树的结点
			arr[idx] = val;		//更新原数组的值
			return;
		}
		int mid = (l + r) >> 1;
		int l_child = 2 * node + 1;
		int r_child = 2 * node + 2;
		if (idx <= mid) {
			update(l_child, l, mid, idx, val);
		}else {
			update(r_child, mid + 1, r, idx, val);
		}
		//对应元素更新好后,更新父节点的值
		tree[node] = tree[l_child] + tree[r_child];
	}

例如,更新 4 号元素为 6,更新后的树如下图
在这里插入图片描述

3.区间查询

  • 当前结点对应的区间若不在查询的范围内,返回 0
  • 查询范围包含了当前结点对应区间的范围,直接返回当前结点的元素

代码:

	/**
	 * @param node	当前结点
	 * @param l		当前结点对应的区间为l~r
	 * @param r
	 * @param start 查询区间的范围为start~end
	 * @param end
	 * @return
	 */
	public static int query(int node, int l, int r, int start, int end) {
		if (start > r || end < l) {	//不在查询的范围
			return 0;
		}
		if (start <= l&& end >= r) {//在查询范围,直接返回
			return tree[node];
		}
		int mid = (l + r) >> 1;
		int l_child = node * 2 + 1;
		int r_child  = node * 2 + 2;
		int l_sum = query(l_child, l, mid, start, end);		//左子树的和
		int r_sum = query(r_child, mid + 1, r, start, end);	//右子树的和
		//返回左子树加右子树的和
		return l_sum + r_sum;
	}

例如查更新后的树的 2~5 号元素的区间和:
在这里插入图片描述

  • 1.查询左子树 [ 0 , 2 ] [0 , 2] [0,2],再查询到其右子树 [ 2 , 2 ] [2,2] [2,2],在查询的区间内,直接返回 5
  • 2.查询右子树 [ 3 , 5 ] [3,5] [3,5],在查询的区间内,直接返回 24.
  • 3.计算左子树和右子树的和 5 + 24 = 29 5 + 24 = 29 5+24=29

线段树的其他区间求最大值、区间修改的方式,与本文的方法类似,就不再赘述,有兴趣的小伙伴可以自行实现

4.完整代码及测试


public class 线段树 {
	static int[] arr = {1,3,5,7,9,11};
	static int[] tree = new int[4 * arr.length];
	public static void main(String[] args) {
		build(0, 0, arr.length - 1);
		for (int i = 0; i < 4 * arr.length; i++) {
			System.out.print(tree[i] + " ");
		}
		System.out.println();
		update(0, 0, arr.length - 1, 4, 6);
		for (int i = 0; i < 4 * arr.length; i++) {
			System.out.print(tree[i] + " ");
		}
		int s = query(0,0,arr.length - 1, 2 , 5);
		System.out.println("\n" + s);
	}
	/**
	 * @param node 当前结点
	 * @param l l和r表示当前的范围
	 * @param r
	 */
	public static void build(int node, int l, int r) {
		if (l == r) {
			tree[node] = arr[l];
			return;
		}
		int mid = (l + r) >> 1;
		int l_child = 2 * node + 1;
		int r_child = 2 * node + 2;
		build(l_child, l, mid);
		build(r_child, mid + 1, r);
		tree[node] = tree[l_child] + tree[r_child];
	}
	/**
	 * @param node 	当前结点
	 * @param l		当前结点对应的区间为l~r
	 * @param r
	 * @param idx	需更新点的下标(原数组下标)
	 * @param val	更新为什么值
	 */
	public static void update(int node, int l, int r, int idx, int val) {
		if (l == r) {	//l=r的时候,表示找到了idx对应的结点
			tree[node] = val;	//更新树的结点
			arr[idx] = val;		//更新原数组的值
			return;
		}
		int mid = (l + r) >> 1;
		int l_child = 2 * node + 1;
		int r_child = 2 * node + 2;
		if (idx <= mid) {
			update(l_child, l, mid, idx, val);
		}else {
			update(r_child, mid + 1, r, idx, val);
		}
		//更新父节点的值
		tree[node] = tree[l_child] + tree[r_child];
	}
	
	/**
	 * @param node	当前结点
	 * @param l		当前结点对应的区间为l~r
	 * @param r
	 * @param start 查询区间的范围为start~end
	 * @param end
	 * @return
	 */
	public static int query(int node, int l, int r, int start, int end) {
		if (start > r || end < l) {	//不在查询的范围
			return 0;
		}
		if (start <= l&& end >= r) {//在查询范围,直接返回
			return tree[node];
		}
		int mid = (l + r) >> 1;
		int l_child = node * 2 + 1;
		int r_child  = node * 2 + 2;
		int l_sum = query(l_child, l, mid, start, end);		//左子树的和
		int r_sum = query(r_child, mid + 1, r, start, end);	//右子树的和
		//返回左子树加右子树的和
		return l_sum + r_sum;
	}
}

测试截图:
在这里插入图片描述

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

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

相关文章

PacBio HiFi 测序动植物基因组项目真实案例测评

HiFi Reads全称High fidelity reads, 是PacBio公司基于Sequel II平台产出的兼具长读长和高准确度的测序序列&#xff0c;该测序模式&#xff08;CCS测序模式&#xff09;一经问世&#xff0c;备受广大组学科研用户关注——其超长读长完美规避了二代测序short reads的天生不足&a…

【密码加密原则三】

目录 1 密码加密原则&#xff08;续&#xff09; 1.1 盐值的优化 1.2 Mybatis中的占位符 1 密码加密原则&#xff08;续&#xff09; 1.1 盐值的优化 为了进一步保障密码安全&#xff0c;可以考虑使用随机的盐值&#xff0c;但是&#xff0c;需要注意&#xff0c;随机的盐…

Java高效率复习-MySQL下篇[MySQL]

前言 本文章的语言描述会比上篇多一些 数据库的创建修改与删除 标识符命名规则 数据库名、表名不得超过30个字符&#xff0c;变量限制为29个必须只能包含A-Z&#xff0c;a-z&#xff0c;0-9&#xff0c;_等63个字符数据库名、表名、字段名等对象名中间不要包含空格同一个My…

生产环境 Nginx后端服务大量TIME-WAIT的解决

netstat -n | awk /^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]} ss -s netstat -nat |awk {print $6}|sort|uniq -c|sort -rn 统计TIME_WAIT 连接的本地地址 netstat -an | grep TIME_WAIT | awk {print $4} | sort | uniq -c | sort -n -k1 尝试抓取 tcp 包 tcpd…

工业CT之三维重建技术

目前&#xff0c;国内现有的工业CT设备绝大多数是基于线阵探测器的断层扫描技术。 该技术主要是通过观察二维图像去发现单层断面上的损伤部位&#xff0c;至于能准确地确定损伤部位的空间位置、大小、几何形状等&#xff0c;仅通过观察二维切片图像是很难实现的。 这个时候就需…

Flink系列之Flink中Checkpoint容错机制

title: Flink系列 三、Flink Checkpoint 容错机制原理概述 ​ Flink 提供了 Exactly once 特性&#xff0c;是依赖于带有 barrier 的分布式快照 可部分重发的数据源功能实现的。而分布式快照中&#xff0c;就保存了 operator 的状态信息。 ​ Flink 的失败恢复依赖于 检查点…

Zabbix技术分享——如何使用zabbix监控华为云RDS

在数字化大背景下&#xff0c;数据是重要的生产资料&#xff0c;这些数据存放在哪里&#xff0c;如何保障数据安全是所有企业都要考虑的事情。华为云RDS凭借安全可靠&#xff0c;可根据业务规模动态扩容的特性&#xff0c;受到越来越多中小企业的青睐&#xff0c;对华为云RDS监…

NR PUSCH power control(一)

这篇看下NR PUSCH power control的相关内容&#xff0c;主要内容集中在38.213 7.1章节&#xff0c;功率计算无非就是一个长公式&#xff0c;根据RRC配置的参数及后续DCI field 的内容作出功率的调整&#xff1b;最初这部分看的就云里雾里的&#xff0c;最近再看&#xff0c;相比…

upload-labs通关

upload-labs通关 shell &#x1f349; 目录upload-labs通关PASS-01、PASS-02PASS-03PASS-04PASS-05PASS-06PASS-07PASS-08PASS-09PASS-10PASS-11PASS-12PASS-13PASS-14PASS-15PASS-16PASS-17PASS-18PASS-19PASS-20PASS-21shell能上传并能解析就算成功 PASS-01、PASS-02 图片…

最近要考pmp,哪个培训机构比较好?

你说的几个都是我着重了解过的&#xff0c;作为过来人&#xff0c;把我做的各大机构的优缺点给你参考吧~ PMP 机构排名的话&#xff0c;没有官方数据&#xff0c;网上数据仅供参考。这篇机构对比的文章&#xff0c;主流机构都有&#xff0c;你可以看看 下面说下我收集的每个机…

【数据库数据恢复】无法启动MongoDB服务的数据恢复案例

关于MongoDB数据库&#xff1a; MongoDB数据库存储方式是将文档存储在集合之中&#xff0c;而不是像Oracle、MySQL一样的关系型数据库。 MongoDB数据库是开源数据库&#xff0c;也提供具有附加功能的商业版本。 MongoDB中的数据是以键值对(key-value pairs)的形式显示的&…

[附源码]Python计算机毕业设计Django校友社交系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

“云办公”如何用任务协同工具搞定项目和团队管理?

导语&#xff1a;远程参加会议、团队协同作业、项目负责人进行任务分配、团队成员多人协同编辑文件及同时推进项目、人力部门在线进行审批报销……&#xff0c;随着“云办公”的加速普及&#xff0c;人们只需一台电脑、一部手机、一根网线&#xff0c;就能随时进入办公状态&…

云服务器及域名到期后,公安联网注销指南

云服务器及域名到期后&#xff0c;公安联网注销指南 公安联网备案及注销的操作流程都写在了官方文档中&#xff0c;可以进入全国互联网安全管理服务平台&#xff0c;在下载中心找到并下载 《互联网站安全服务平台操作指南》&#xff0c;按照操作指南进行备案及撤销。 以下图…

JS实现关闭图片窗口

JS实现关闭图片窗口 有趣的小案例池子&#xff1a; JS实现定时器 JS实现关闭图片窗口 JS实现输入检验 获取焦点后隐藏提示内容的输入框 JS实现获取鼠标在画布中的位置 聊天信息框显示消息 JS点击切换背景图 自动切换背景的登录页面 JS制作跟随鼠标移动的图片 JS实现记住用…

K8S Pod控制器详细讲解

文章目录一、Pod控制器介绍二、ReplicaSet(RS)三、Deployment(Deploy)1.镜像更新&#xff1a;2.版本回退3.金丝雀发布/灰度发布四、Horizontal Pod Autoscaler(HPA)五、DaemonSet(DS)六、Job七、CronJob(CJ)结尾一、Pod控制器介绍 Pod是kubernetes的最小管理单元&#xff0c;在…

ArcGIS矢量化并进行拓扑检查

土地利用数据每年都在发生变化&#xff0c;故每年都要根据去年的数据进行修改。请根据以下要求&#xff0c;修改A区域的数据并对B区域已做好的数据进行拓扑检查。 01 数据说明 1. 地类图斑A.shp&#xff1a;A区域需要编辑修改的图斑数据。 2. 影像.tif&#xff1a;编辑A区域…

Docker数据卷自定义Docker镜像

目录 宿主机与容器之间的文件拷贝 引言&#xff1a;利用MySQL镜像安装MySQL服务 从容器中拷贝文件到宿主机 从宿主机拷贝文件到容器 数据卷 数据卷容器 Dockerfile自定义镜像 自定义tomcat8&#xff08;熟悉几乎所有的Dockerfile命令&#xff09; 宿主机与容器之间的文…

集群配置步骤_java培训

配置步骤 复制3个ZooKeeper zookeeper-3.4.9.tar.gz解压后拷贝到/myzookeeper目录下并重新名为zk01&#xff0c;再复制zk01形成zk02、zk03&#xff0c;共计3份 新增目录 进入zk01/02/03分别新建文件夹&#xff0c;mydata、mylog 新建配置文件 分别进入zk01-zk03各自的conf文件…

perflab 课程设计

初始状态 rotate 版本I 因为本题步步都在寻址而寻址的目标每一步又不同&#xff0c;并且在一个地址的值一次就赋值完毕&#xff0c;不会对同一个地址进行二次寻址&#xff0c;所以我首先想的改进方向就是使得寻址更加快速&#xff0c;于是我使得寻址的地址更加连续。 int i,…