线段树

  • 一、线段树概念
  • 二、线段树模板
    • 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 2∗k+1 的元素代表左儿子,用下标 2 ∗ k + 2 2 * k +2 2∗k+2 的元素代表右儿子来进行树的模拟

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

二、线段树模板

模板题:操作格子

1.建树

  • 线段树建树的操作跟二叉树的建树操作很类似,都利用递归,构建左儿子和右儿子。
  • 任意一个结点 k k k,它的左儿子为第 2 ∗ k + 1 2 * k +1 2∗k+1 个元素,右儿子为第 2 ∗ k + 2 2 * k +2 2∗k+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;}
}

测试截图:

线段树模板(Java)相关推荐

  1. hdu1156(简单线段树 模板题)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  2. 线段树模板hdu 1754:I Hate It

    I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  3. 试题 算法训练 操作格子(线段树模板题)

    资源限制 内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 问题描述 有n个格子,从左到右放成一排,编号为1-n. 共有m次操作,有3 ...

  4. hdu1754 I hate it线段树模板 区间最值查询

    题目链接:这道题是线段树,树状数组最基础的问题 两种分类方式:按照更新对象和查询对象 单点更新,区间查询; 区间更新,单点查询; 按照整体维护的对象: 维护前缀和; 维护区间最值. 线段树模板代码 # ...

  5. 【AHOI2009】【BZOJ1798】Seq 维护序列seq(线段树模板,易错提醒)

    problem 给定一个长为n的序列,m次询问 每次询问有3种操作 1.一段区间全部乘一个值 2.一段区间全部加一个值 3.询问一段区间和%P solution 不就一颗线段树么,看朕10分钟A掉.. ...

  6. 洛谷3373 线段树模板

    题目详情:https://www.luogu.org/problemnew/show/P3373 这个线段树模板写的头疼(最后纠错发现一个long long没开差点一口血喷出来),思路就是在普通的求区 ...

  7. ccf除法-线段树模板

    这题的要求是对区间处理,查询区间和,一看就是线段树模板题,下面是代码 #include<iostream> using namespace std; int pp[100001]; str ...

  8. 【线段树】[LUOGU 守墓人] [LUOGU 维护序列] 线段树模板题

    题目: 题目链接:[LUOGU 守墓人] 题解: 线段树单点修改,区间修改,单点查询,区间查询,一系列线段树基本操作,模板打就好. (回头再补一个分块和树状数组的这种板子题,就是用分块和树状数组再写一 ...

  9. 线段树模板题3:区间染色问题

    1.3线段树模板题3:区间染色问题 在DotA游戏中,帕吉的肉钩实际上是大多数英雄中最恐怖的东西.挂钩由长度相同的几个连续的金属棍组成. 现在,帕吉(Pudge)希望对挂接进行一些操作. 让我们将钩子 ...

最新文章

  1. python 原始字符串r的用法
  2. 伯克利电子和计算机工程申请入口
  3. linux C 进程内部存储管理
  4. hashmap删除指定key_HashTable和HashMap的区别详解
  5. Android 实现切换主题皮肤功能(类似于众多app中的 夜间模式,主题包等)
  6. 银行营业网点管理系统——dao包(BaseDao)
  7. linux下svn常用指令(转)
  8. 使用 gradle 在编译时动态设置 Android resValue / BuildConfig / Manifes中lt;meta-datagt;变量的值...
  9. python的sorted函数和operator.itemgetter函数
  10. Hibernate教程01
  11. 计算机辅助翻译软件差异,两款计算机辅助翻译软件的比较
  12. 3.9 使用标尺工具拉直倾斜的图片 [Ps教程]
  13. 打开.class文件
  14. 网络模型早停earlystopping详解
  15. 2021-09-30 node上传文件的问题以及如何选择适合自己的插件
  16. 联想笔记本键盘排线_笔记本键盘排线怎么拆 thinkpad
  17. javascript之操作文件(Files)
  18. View之背景虚化处理(头像作为背景模糊化处理)
  19. 关于js数组方法的题目整理 6.24更新至题目5
  20. Python3利用VirusTotal的vt库通过API上传样本

热门文章

  1. 计算机毕业设计Android社区物业便民服务小区APP设计(源码+系统+mysql数据库+Lw文档)
  2. web小游戏开发:华容道(一)
  3. java智能推荐系统技术_GitHub - newfishwang/mirs: 电影智能推荐系统JAVA实现
  4. win11 系统 GPU版本pytorch、CUDA、anaconda 、pycharm详细安装教程
  5. 目标检测模型设计准则 | YOLOv7参考的ELAN模型解读,YOLO系列模型思想的设计源头
  6. 图片高度固定,宽度自适应,最大充满设定宽度,多余裁剪
  7. linux下路径怎么写,linux相对路径怎么写 linux的相对路径怎么表示?
  8. Java Mybatis 框架 VII 之 Mybatis 扩展
  9. 大王币说 | “IPFS中国区教父”周欢:散户怎样才能在IPFS中获利
  10. kepware OPC UA