LeetCode_线段树_中等_307.区域和检索 - 数组可修改

目录

  • 1.题目
  • 2.思路
  • 3.代码实现(Java)

1.题目

给你一个数组 nums ,请你完成两类查询。

  • 其中一类查询要求 更新 数组 nums 下标对应的值
  • 另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 ,其中 left <= right

实现 NumArray 类:

  • NumArray(int[] nums) 用整数数组 nums 初始化对象
  • void update(int index, int val) 将 nums[index] 的值 更新 为 val
  • int sumRange(int left, int right) 返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 (即,nums[left] + nums[left + 1], …, nums[right])

示例 1:
输入:
[“NumArray”, “sumRange”, “update”, “sumRange”]
[[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
输出:
[null, 9, null, 8]
解释:

NumArray numArray = new NumArray([1, 3, 5]);
numArray.sumRange(0, 2); // 返回 1 + 3 + 5 = 9
numArray.update(1, 2);   // nums = [1,2,5]
numArray.sumRange(0, 2); // 返回 1 + 2 + 5 = 8

提示:
1 <= nums.length <= 3 * 104
-100 <= nums[i] <= 100
0 <= index < nums.length
-100 <= val <= 100
0 <= left <= right < nums.length
调用 update 和 sumRange 方法次数不大于 3 * 104

2.思路

(1)前缀和
使用前缀和的思想,来维护原始数组的区间和。

  • 在构造函数中,首先初始化原始数组 nums,然后计算前缀和 preSum,用于记录 0 至 i (包括i) 的和。这里 preSum 的长度比 nums 的长度大 1,是因为第一个前缀和 preSum[0] 被初始化为 0,用于方便后续的计算。
  • 更新操作需要更新原始值和前缀和数组,即当 nums[index] 更新为 val 时,从 index + 1 开始,对于 preSum 数组中的每一项,更新为原先的值加上该位置对应原始值变化后的差值。
  • 查询操作使用前缀和的思想,其中 preSum[right + 1] 表示 0 至 right 的和,preSum[left] 表示 0 至 left - 1 的和,两者相减即得[left, right] 的和。

这种方法的时间复杂度为构造对象时为 O(n),更新操作为 O(n),查询操作为 O(1),空间复杂度为 O(n)。

(2)分块处理
思路参考本题官方题解。

具体实现是,在构造方法中,首先将原始数组分成若干个大小为 n \sqrt{n} n 的块( n n n 为原始数组的长度),并对每个块计算出其元素和,保存在 sum 数组中。这样,可以通过查询两个块之间元素和的和来计算区间和。对于区间查询,可以分为三种情况:

  • 区间 [left, right] 在同一个块中,直接顺序遍历该块内的元素求和并返回。
  • 区间 [left, right] 跨越若干个块,分别计算每个块"首尾元素"与这两个元素之间的元素和,再将结果相加得到区间和。特别的,对第一个块位于区间左边的部分,只计算其位于区间内的部分;对最后一个块位于区间右边的部分,只计算其位于区间内的部分。
  • 如果区间 [left, right] 在一个块的左边、右边、甚至它本不在任何一个块中,那就和情况2一样分别计算。

对于单点修改,按照数组的更新方式更新每个块的元素和即可。

(3)线段树
思路参考本题官方题解。

3.代码实现(Java)

//思路1————前缀和
class NumArray {private int[] nums;private int[] preSum;public NumArray(int[] nums) {int n = nums.length;this.nums = Arrays.copyOf(nums, n);preSum = new int[n + 1];for (int i = 1; i < n + 1; i++) {preSum[i] = preSum[i - 1] + nums[i - 1];}}public void update(int index, int val) {int original = nums[index];nums[index] = val;for (int i = index + 1; i < preSum.length; i++) {preSum[i] += val - original;}}public int sumRange(int left, int right) {return preSum[right + 1] - preSum[left];}
}/*** Your NumArray object will be instantiated and called as such:* NumArray obj = new NumArray(nums);* obj.update(index,val);* int param_2 = obj.sumRange(left,right);*/
//思路2————线段树
class NumArray {// sum[i] 表示第 i 个块的元素和private int[] sum; //块的大小private int size;private int[] nums;public NumArray(int[] nums) {this.nums = nums;int n = nums.length;size = (int) Math.sqrt(n);// n / size 向上取整sum = new int[(n + size - 1) / size]; for (int i = 0; i < n; i++) {sum[i / size] += nums[i];}}public void update(int index, int val) {sum[index / size] += val - nums[index];nums[index] = val;}public int sumRange(int left, int right) {// left 位于第 b1 个块内int b1 = left / size;// left 在第 b1 个块内的偏移量int i1 = left % size;// right 位于第 b2 个块内int b2 = right / size;// right 在第 b2 个块内的偏移量int i2 = right % size;//区间 [left, right] 在同一块中if (b1 == b2) { int sum = 0;for (int j = i1; j <= i2; j++) {sum += nums[b1 * size + j];}return sum;}//计算第 b1 个块位于区间 [i1, size - 1) 的元素和 sum1int sum1 = 0;for (int j = i1; j < size; j++) {sum1 += nums[b1 * size + j];}//计算第 b2 个块位于区间 [0, i2] 的元素和 sum2int sum2 = 0;for (int j = 0; j <= i2; j++) {sum2 += nums[b2 * size + j];}//计算第 b1 + 1 个块到第 b2 − 1 个块的元素和的总和 sum3int sum3 = 0;for (int j = b1 + 1; j < b2; j++) {sum3 += sum[j];}return sum1 + sum2 + sum3;}
}/*** Your NumArray object will be instantiated and called as such:* NumArray obj = new NumArray(nums);* obj.update(index,val);* int param_2 = obj.sumRange(left,right);*/
//思路3————线段树
class NumArray {//用于存储区间和的线段树private int[] segmentTree; //原始数组的长度private int n; public NumArray(int[] nums) {n = nums.length;//线段树的大小是原始数组长度的 4 倍(稍微比需要的最小长度大一些)segmentTree = new int[nums.length * 4]; //构建线段树build(0, 0, n - 1, nums); }public void update(int index, int val) {//更新指定索引处的元素值change(index, val, 0, 0, n - 1); }public int sumRange(int left, int right) {//求解指定区间的和return range(left, right, 0, 0, n - 1);}//构建线段树的递归函数private void build(int node, int s, int e, int[] nums) {if (s == e) {//叶节点,保存原始数组的元素值segmentTree[node] = nums[s]; return;}int m = s + (e - s) / 2;//递归构建左子树build(node * 2 + 1, s, m, nums); //递归构建右子树build(node * 2 + 2, m + 1, e, nums); //更新父节点的值为左子树和右子树的和segmentTree[node] = segmentTree[node * 2 + 1] + segmentTree[node * 2 + 2]; }//更新线段树的递归函数private void change(int index, int val, int node, int s, int e) {if (s == e) {//叶节点,更新原始数组的元素值segmentTree[node] = val; return;}int m = s + (e - s) / 2;if (index <= m) {//目标索引在左子树中,递归更新左子树change(index, val, node * 2 + 1, s, m);} else {//目标索引在右子树中,递归更新右子树change(index, val, node * 2 + 2, m + 1, e); }segmentTree[node] = segmentTree[node * 2 + 1] + segmentTree[node * 2 + 2]; // 更新父节点的值为左子树和右子树的和}//查询线段树中指定区间范围的和的递归函数private int range(int left, int right, int node, int s, int e) {if (left == s && right == e) {//找到了目标区间,返回当前节点的值return segmentTree[node]; }int m = s + (e - s) / 2;if (right <= m) {//目标区间完全在左子树中,递归查询左子树return range(left, right, node * 2 + 1, s, m); } else if (left > m) {//目标区间完全在右子树中,递归查询右子树return range(left, right, node * 2 + 2, m + 1, e); } else {//目标区间跨越左右子树,分别递归查询左右子树并求和return range(left, m, node * 2 + 1, s, m) + range(m + 1, right, node * 2 + 2, m + 1, e); }}
}

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

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

相关文章

关于tcp发送成功但对端无法接收情况的思考

用到一个http服务&#xff0c;但调用频率很高&#xff0c;每次请求都使用短连接的话&#xff0c;有点浪费。 所以尝试复用http连接&#xff0c;请求的时候在头部添加Connection&#xff1a;Keep-alive&#xff0c;对端支持&#xff0c;但会在一定时常或一定请求次数后关闭该连接…

压测工具主要功能是什么?该怎样选择?

压测工具是一类用于模拟并评估系统在不同负载条件下的性能的软件应用程序。通过模拟大量用户同时访问系统&#xff0c;压测工具能够帮助开发者识别系统的瓶颈、性能瓶颈以及潜在的故障点。这种实时、模拟的方式允许开发者在正式投入使用之前发现并解决问题&#xff0c;提高系统…

es 报错 Data too large 触发断路器

文章目录 [toc]事出有因解决思路效果展示关于重启课外扩展 事出有因 报错原因是 es 在 full GC 之前触发了默认的断路器&#xff0c;导致报错 [parent] Data too large&#xff0c;相似的报错内容如下&#xff1a; Caused by: org.elasticsearch.common.breaker.CircuitBreakin…

牛客--完全数计算python

完全数&#xff08;Perfect number&#xff09;&#xff0c;又称完美数或完备数&#xff0c;是一些特殊的自然数。 它所有的真因子&#xff08;即除了自身以外的约数&#xff09;的和&#xff08;即因子函数&#xff09;&#xff0c;恰好等于它本身。 例如&#xff1a;28&…

使用JDBC连接数据库出现The server time zone value ‘�й���׼ʱ��‘ is unrecognized 的解决方案

看到网上的大佬们说是引入的依赖版本太高所以导致了时区有问题 但是我把依赖的版本改低了还是报错 用另一种办法直接在配置文件中修改url然后成功解决 spring:datasource:url: jdbc:mysql://127.0.0.1:3306/datasource?useUnicodetrue&characterEncodingutf8&useSSL…

K8S在任意节点使用kubectl

1、将master节点中的配置文件拷贝到node节点。 [rootk8s-master-10 kubernetes]# scp /etc/kubernetes/admin.conf rootk8s-node-12:/etc/kubernetes/2、在对应服务器上配置环境变量 [rootk8s-node-12 ~]# echo "export KUBECONFIG/etc/kubernetes/admin.conf">…

Win Docker Desktop + WSL2 部署PyTorch-CUDA服务至k8s算力集群

Win Docker Desktop WSL2 部署PyTorch-CUDA服务至k8s算力集群 Win Docker Desktop WSL2 安装安装WSL-Ubuntu拉取镜像并测试挂载数据并开放端口导出镜像或导入镜像在k8s集群部署 Win Docker Desktop WSL2 安装 首先根据你的操作系统版本 安装WSL &#xff0c;记得切换WSL2&a…

vue day1(主要是指令)

1、引包 或者&#xff1a;cdn网址 2、创建实例&#xff0c;初始化渲染 3、插值表达式 {{}} 表达式&#xff1a;可以被求值的代码 4、响应式数据&#xff1a;数据发生变化&#xff0c;视图自动更新&#xff08;底层是dom操作&#xff09; data中数据会被添加到实例上&#x…

解决Dockerfile中 Could not initialize class sun.awt.X11FontManager错误

Dockerfile中增加命令 RUN yum install dejavu-sans-fonts fontconfig -y如果您使用的是基于Alpine Linux的发行版&#xff0c;可以使用apk命令来安装DejaVu Sans字体和fontconfig工具 RUN apk update RUN apk add ttf-dejavu fontconfig

设计数据库的时候会考虑哪些因素,怎样去建表?

在设计数据库时&#xff0c;通常会考虑以下因素&#xff1a; 数据的结构和关系&#xff1a;首先需要分析业务需求&#xff0c;了解需要存储的数据类型、数据之间的关系以及数据的组织结构。 数据的完整性和一致性&#xff1a;确保数据库中的数据完整性和一致性&#xff0c;例如…

CSDN规则详解——如何申请成为博客专家

文章目录 前言博客专家如何成为博客专家&#xff1f;博客专家列表后记 前言 博客专家是csdn推出的&#xff0c;很多童鞋可能还不知道如何申请成为博客专家或者成为博客专家之后有什么用。成为博客专家可以让您在专业领域分享您的知识和经验&#xff0c;与更多的读者建立联系&a…

服务器如何下载百度网盘数据

百度网盘作为镜像 国外用户传数据到我们服务器比较慢,但是传输百度网盘速度还是可以的。 这样我们就可以将百度网盘作为一个文件中转站。 但Linux系统下使用百度网盘有些麻烦,虽然百度网盘也有Linux版本,但服务器没开启图形界面,使用的是命令行。这个时候就得感谢开发者Ho…

如何进行iOS技术博客的备案?

​ 如何进行iOS技术博客的备案&#xff1f; 标题&#xff1a;iOS技术博客备案流程及要求解析 摘要&#xff1a; 在本篇问答中&#xff0c;我们将为iOS技术博主介绍如何进行备案。如果你的iOS应用只包含简单的页面&#xff0c;并通过蓝牙进行数据采集和传输&#xff0c;那么你…

Rust编程中的共享状态并发执行

1.共享状态并发 虽然消息传递是一个很好的处理并发的方式&#xff0c;但并不是唯一一个。另一种方式是让多个线程拥有相同的共享数据。在学习Go语言编程过程中大家应该听到过一句口号:"不要通过共享内存来通讯"。 在某种程度上&#xff0c;任何编程语言中的信道都类…

单链表的插入删除

#include <iostream>#include <stdio.h> #include <stdlib.h>using namespace std;//带头指针的单链表typedef struct LNode{int data;struct LNode *next;}LNode, *LinkList;bool InitList(LinkList &L){L (LNode *) malloc(sizeof(LNode));if(L NUL…

消息队列简介

什么是消息队列?&#xff08;Message queue&#xff0c;简称MQ&#xff09; 从字面理解就是一个保存消息的一个容器。那么我们为何需要这样一个容器呢&#xff1f; 其实就是为了解耦各个系统&#xff0c;我们来举个例子&#xff1a; 有这么一个简单的场景&#xff0c;系统A负…

Power Automate-与Microsoft Forms连接

创建自动化云端流&#xff0c;流的触发器选择第一个提交新回复时 点击蓝色的Change connection&#xff0c;登录创建Microsoft Forms表单的账号 选择提前创建的表单&#xff1b;如果想连接其他账号创建的Microsoft Forms表单&#xff0c;可以再次点击蓝色的Change connection&a…

DVWA - 3

文章目录 XSS&#xff08;Dom&#xff09;lowmediumhighimpossible XSS&#xff08;Dom&#xff09; XSS 主要基于JavaScript语言进行恶意攻击&#xff0c;常用于窃取 cookie&#xff0c;越权操作&#xff0c;传播病毒等。DOM全称为Document Object Model&#xff0c;即文档对…

Flutter开发实战之上传身份照片并认证

思路 UI视图 上传身份证照片可以选择拍照方式上传,相册选择方式上传即可 身份证照片进行认证功能实现 对身份证照片进行认证,包括正面认证和反面认证即可上传给后端 使用第三方插件 image_picker: ^0.8.4Future<XFile> _getCameraImage() async {final cameraImages = …

【k8s集群搭建(一):基于虚拟机的linux的k8s集群搭建_超详细_解决并记录全过程步骤以及自己的踩坑记录】

虚拟机准备3台Linux系统 k8s集群安装 每一台机器需要安装以下内容&#xff1a; docker:容器运行环境 kubelet:控制机器中所有资源 bubelctl:命令行 kubeladm:初始化集群的工具 Docker安装 安装一些必要的包&#xff0c;yum-util 提供yum-config-manager功能&#xff0c;另两…