数据结构 - 线段树

1. 预制值:

  • 构建的数组为,nums:【2, 5, 1, 4, 3】
  • 区间和问题,假设求区间 [1,3] 的和

2. 建树

2.1 构建线段树数组

int[] segT = new int[4*n]

(为什么数组大小是4*n???评论区欢迎留言)
在这里插入图片描述

tips:长方格中的left、right,分别代表该节点所求得的区间和。例如,left:0,right:4。代表nums中索引0到4的和=2+5+1+4+3=15。

在这里插入图片描述

  • 核心代码
public void build(int index, int left, int right) {// 2.1.1的工作,递归条件,不可再分区间if (left == right) {segT[index] = nums[left];return;}// 2.1.1的工作,区间多次二分int mid = (right - left) / 2 + left;// 左子树根节点的索引int leftIndex = index * 2;// 右子树根节点的索引int rightIndex= index * 2 + 1;// 构建左、右子树build(leftIndex, left, mid);build(rightIndex, mid+1, right);// 2.1.2的工作,求根节点的和// tips:只有当left==right返回后,才会走这一步segT[index] = segT[leftIndex] + segT[rightIndex];}

3. 更新

逻辑其实和建树一样,在线段树中找到修改节点的对应索引,然后修改其值,然后再依次修改根节点的值即可。
在这里插入图片描述

  • 核心代码
public void update(int index, int start, int end, int updateIndex, int value) {// 找到叶子节点,直接修改值(不可再分区间了)if (start == end) {nums[updateIndex] = value;segT[index] = value;return;} int mid = (start + end) / 2;int leftIndex = index * 2;int rightIndex = index * 2 + 1;// 修改的节点再左子树还是右子树if (updateIndex >= start && updateIndex <= mid) {update(leftIndex, start, mid, updateIndex, value);} else {update(rightIndex, mid + 1, end, updateIndex, value);}segT[index] = segT[leftIndex] + segT[rightIndex];}

4. 查询

查询的逻辑会稍微复杂一点,因为有三个因素:

4.1 查询的范围全落在左子树

在这里插入图片描述

4.2 查询的范围全落在右子树

在这里插入图片描述

4.3 查询的范围既在左子树、又在右子树

在这里插入图片描述

  • 核心代码
public int querySum(int index, int start, int end, int left, int right) {// 查询的索引无效(不是线段树的有意义范围)if (left > end || right < start) {return 0;}// 查询的范围包含该子树的范围,直接返回即可(见下图【包含子树】)else if (left <= start && right >= end) {return st[index].sum;}// 求的左右子树的索引int mid = (start + end) / 2;int leftIndex = index * 2;int rightIndex = index * 2 + 1;// 左、右子树求得的值int leftSum = querySum(leftIndex, start, mid, left, right);int rightSum = querySum(rightIndex, mid + 1, end, left, right);return leftSum + rightSum;}

在这里插入图片描述
图:包含子树

5. 线段树求区间问题模板(最大值、最小值、和)

public class SegmentTree {class Node {int left;int right;int max;int min;int sum;Node() {}Node (int left, int right) {this.left = left;this.right = right;this.max = Integer.MIN_VALUE;this.min = Integer.MAX_VALUE;this.sum = 0;}}int n;Node[] st;int[] nums;public void build(int index, int left, int right) {if (left == right) {st[index].sum = nums[left];st[index].max = nums[left];st[index].min = nums[left];return;}int mid = (right - left) / 2 + left;int leftIndex = index * 2;int rightIndex= index * 2 + 1;build(leftIndex, left, mid);build(rightIndex, mid+1, right);st[index].max = Math.max(st[leftIndex].max, st[rightIndex].max);st[index].min = Math.min(st[leftIndex].min, st[rightIndex].min);st[index].sum = st[leftIndex].sum + st[rightIndex].sum;}public void init(int N, int[] arr) {n = N;st = new Node[4 * n + 1];for (int i = 1; i <= 4 * n; i++) {st[i] = new Node();}nums = arr;}public int querySum(int left, int right) {return querySum(1, 0, n-1, left, right);}public int querySum(int index, int start, int end, int left, int right) {if (left > end || right < start) {return 0;} else if (left <= start && right >= end) {return st[index].sum;}int mid = (start + end) / 2;int leftIndex = index * 2;int rightIndex = index * 2 + 1;int leftSum = querySum(leftIndex, start, mid, left, right);int rightSum = querySum(rightIndex, mid + 1, end, left, right);return leftSum + rightSum;}public int queryMax(int left, int right) {return queryMax(1, 0, n-1, left, right);}public int queryMax(int index, int start, int end, int left, int right) {if (left > end || right < start) {return 0;} else if (left <= start && right >= end) {return st[index].sum;}int mid = (start + end) / 2;int leftIndex = index * 2;int rightIndex = index * 2 + 1;int leftMax = queryMax(leftIndex, start, mid, left, right);int rightMax = queryMax(rightIndex, mid + 1, end, left, right);return Math.max(leftMax, rightMax);}public int queryMin(int left, int right) {return queryMin(1, 0, n-1, left, right);}public int queryMin(int index, int start, int end, int left, int right) {if (left > end || right < start) {return Integer.MAX_VALUE;} else if (left <= start && right >= end) {return st[index].sum;}int mid = (start + end) / 2;int leftIndex = index * 2;int rightIndex = index * 2 + 1;int leftMin = queryMin(leftIndex, start, mid, left, right);int rightMin = queryMin(rightIndex, mid + 1, end, left, right);return Math.min(leftMin, rightMin);}public void update(int index, int start, int end, int updateIndex, int value) {if (start == end) {nums[updateIndex] = value;st[index].sum = value;st[index].max = value;st[index].min = value;return;} int mid = (start + end) / 2;int leftIndex = index * 2;int rightIndex = index * 2 + 1;if (updateIndex >= start && updateIndex <= mid) {update(leftIndex, start, mid, updateIndex, value);} else {update(rightIndex, mid + 1, end, updateIndex, value);}st[index].max = Math.max(st[leftIndex].max, st[index].max);st[index].min = Math.min(st[leftIndex].min, st[index].min);st[index].sum = st[leftIndex].sum + st[index].sum;}public void update(int index, int value) {nums[index] = value;update(1, 0, n - 1, index, value);}}

6. 例题

leetcode307 区域和检索 - 数组可修改

7. 注意

并不是所有求区间和都能用线段树,例如leetcode327 区间和的个数,元素的值范围是 -231<=num<= 231-1,如果直接构建线段树,空间复杂度会挤爆!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R语言rmarkdown使用

1、安装 install.packages(rmarkdown) library(rmarkdown) install.packages(tinytex) tinytex::install_tinytex() 2、新建R Markdown 3、基本框架 红色框内为YAML&#xff1a;包括标题、作者和日期等 黄色框内为代码块&#xff1a;执行后面的代码&#xff0c;并可以设置展…

在vscode 中配置 pyside6 环境

在vscode中编写pyside环境配置 start 记录一下在 vscode 中编写 pyside6 程序&#xff0c;环境如何配置。 前提 请自行安装好 python。请自行安装好 vscode。安装 vscode 插件 Python&#xff0c;PYQT Integration。 配置环境 1.借助 pip 安装我们的pyside6 pip install…

WPF中值转换器的使用

什么是值转换器 在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;值转换器&#xff08;Value Converter&#xff09;是一种机制&#xff0c;允许你在绑定时转换绑定源和绑定目标之间的值。值转换器实现了 IValueConverter 接口&#xff0c;该接口…

飞天使-k8s知识点15-kubernetes散装知识点4-CNI网络插件与kubectl

文章目录 CNI 网络插件安装任意节点运行kubectlAPI的版本区别与废弃API查询 CNI 网络插件安装 这里将以 Calico 为例&#xff0c;提供在 Kubernetes 1.20.6 版本上安装 CNI 插件的步骤。请注意&#xff0c;具体的步骤可能会因 CNI 插件的类型和你的特定环境而略有不同。设置 Ku…

蓝桥杯每日一练(python)B组

###来源于dotcpp的蓝桥杯真题 题目 2735: 蓝桥杯2022年第十三届决赛真题-取模&#xff08;Python组&#xff09; 给定 n, m &#xff0c;问是否存在两个不同的数 x, y 使得 1 ≤ x < y ≤ m 且 n mod x n mod y 。 输入格式&#xff1a; 输入包含多组独立的询问。 第一…

终端命令提示符:如何查看我们电脑端口是否被占用和处理方式

文章目录 端口信息查看1、Windows:2、Linux/macOS: 使用 netstat使用 lsof 端口信息查看 在不同的操作系统中&#xff0c;查看端口是否被占用的指令有所不同。以下是一些常见的指令&#xff1a; 1、Windows: 使用命令行工具 netstat 来查看端口占用情况。 电脑键盘按住 win…

gtkmm4 应用程序使用 CSS 样式

文章目录 前言css选择器css文件示例源代码效果 前言 程序样式和代码逻辑分离开 使代码逻辑更可观 css选择器 Cambalache提供了两种css-classes 相当于css里的类名:class“类名”css-name 相当于css里的标签名:spin div p 啥的 如上我设置了这个按钮控件的类名为testButton 标…

边缘计算初创公司ZEDEDA获7200万美元C轮融资,助力边缘计算市场扩张!

边缘计算领域传来喜讯&#xff01; 边缘计算社区获悉&#xff0c;就在昨天&#xff08;2月7日&#xff09;&#xff0c;边缘计算企业ZEDEDA成功募集7200万美元C轮融资&#xff0c;折合人民币高达约5.18亿元。据悉&#xff0c;此次融资使ZEDEDA的估值飙升至4亿美元&#xff0c;相…

《低功耗方法学》翻译——附录B:UPF命令语法

附录B&#xff1a;UPF命令语法 本章介绍了文本中引用的所选UPF命令的语法。 节选自“统一电源格式&#xff08;UPF&#xff09;标准&#xff0c;1.0版”&#xff0c;经该Accellera许可复制。版权所有&#xff1a;(c)2006-2007。Accellera不声明或代表摘录材料的准确性或内容&…

【03】C++ 类和对象 2:默认成员函数

文章目录 &#x1f308; 前言&#x1f308; Ⅰ 构造函数1. 构造函数概念2. 构造函数特性3. 初始化列表 &#x1f308; Ⅱ 析构函数1. 析构函数概念2. 析构函数特性 &#x1f308; Ⅲ 拷贝构造1. 拷贝构造概念2. 拷贝构造特性3. 深度拷贝构造 &#x1f308; Ⅳ 赋值重载1. 运算符…

【LeetCode每日一题】525连续数组 303区域和检索(前缀和的基本概念和3个简单案例)

前缀和 // 构造prefix let prefix [0] arr.forEach(num > {prefix.push(prefix.at(-1) num); })如果想要计算某个区间 i 到 j 这个子数组的和时&#xff0c;可以根据 prefix[j1] - prefix[i] 获得。 例题1&#xff1a;303.区域和检索 - 数组不可变 给定一个整数数组 num…

3分钟部署完成Docker Registry及可视化管理工具Docker-UI

安装docker-registry 由于镜像文件会非常占用空间&#xff0c;因此需要选择一个磁盘充裕的位置来存放镜像数据。 这里设置为&#xff1a;-v /data/registry:/var/lib/registry&#xff0c;其中/data/registry是宿主机存放数据的位置。 docker run -d -p 5000:5000 --restart…