307区域和检索 - 数组可修改(线段树)

2023-10-04 21:10

本文主要是介绍307区域和检索 - 数组可修改(线段树),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、题目描述

给定一个整数数组  nums,求出数组从索引 i 到 j  (i ≤ j) 范围内元素的总和,包含 i,  j 两点。

update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。

说明:

  • 数组仅可以在 update 函数下进行修改。
  • 你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。

2、示例

Given nums = [1, 3, 5]sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8

3、题解

基本思想:线段树

  • 建立线段树:将nums值放入tree最后面作为叶子节点,然后不断更新父节点等于子节点值之和tree[i]=tree[2*i]+tree[2*i+1]
  • 更新线段树:将叶子节点及其往上的节点直到根节点都要更新,时间复杂度O(logn)
  • 查找检索线段树:如果左边界是右子节点,将该节点加入到sum否则往上查找其父节点值,如果右边界是左子节点,将该节点加入到sum否则往上查找其父节点值,这样不断往上查找直到左右边界相遇,时间复杂度O(logn)
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
class NumArray {
public:NumArray(vector<int>& nums) {if(nums.size()==0)  return;sum.resize(nums.size());sum[0]=nums[0];for(int i=1;i<nums.size();i++)sum[i]=sum[i-1]+nums[i];}void update(int i, int val) {int diffval=i==0?val-sum[i]:val-(sum[i]-sum[i-1]);if(diffval==0)  return;for(int j=i;j<sum.size();j++)sum[j]+=diffval;}int sumRange(int i, int j) {return i==0?sum[j]:sum[j]-sum[i-1];}
private:vector<int> sum;
};
class NumArray1 {
public:NumArray1(vector<int>& nums) {//建立线段树:将nums值放入tree最后面作为叶子节点,然后不断更新父节点等于子节点值之和tree[i]=tree[2*i]+tree[2*i+1]if(nums.size()==0)  return;n=nums.size();tree.resize(2*n);for(int i=0,j=n;i<nums.size();i++,j++)tree[j]=nums[i];for(int i=n-1;i>0;i--)tree[i]=tree[2*i]+tree[2*i+1];}void update(int i, int val) {//更新线段树:将叶子节点及其往上的节点直到根节点都要更新,时间复杂度O(logn)int pos=i+n;int diffval=val-tree[pos];if(diffval==0)  return;while(pos>0){tree[pos]+=diffval;pos/=2;}}int sumRange(int i, int j) {//查找检索线段树:如果左边界是右子节点,将该节点加入到sum否则往上查找其父节点值,//如果右边界是左子节点,将该节点加入到sum否则往上查找其父节点值,这样不断往上查找直到左右边界相遇,时间复杂度O(logn)int sum=0;i+=n;j+=n;while(i<=j){//如果左边界是右子节点,将该节点加入到sum这样避免误加左子节点值if(i%2==1){sum+=tree[i];i++;}//如果右边界是左子节点,将该节点加入到sum这样避免误加右子节点值if(j%2==0){sum+=tree[j];j--;}//之后就更新边界到其父节点通过加其父节点值就加了两个子节点值,这样时间复杂度只有O(logn)i/=2;j/=2;}return sum;}
private:vector<int> tree;int n;
};
int main()
{vector<int> nums={1,3,5};NumArray1* obj = new NumArray1(nums);cout<<obj->sumRange(0,2)<<endl;obj->update(1,2);cout<<obj->sumRange(0,2)<<endl;return 0;
}

 

这篇关于307区域和检索 - 数组可修改(线段树)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/2729

相关文章

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

Docker镜像修改hosts及dockerfile修改hosts文件的实现方式

《Docker镜像修改hosts及dockerfile修改hosts文件的实现方式》:本文主要介绍Docker镜像修改hosts及dockerfile修改hosts文件的实现方式,具有很好的参考价... 目录docker镜像修改hosts及dockerfile修改hosts文件准备 dockerfile 文

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Linux修改pip临时目录方法的详解

《Linux修改pip临时目录方法的详解》在Linux系统中,pip在安装Python包时会使用临时目录(TMPDIR),但默认的临时目录可能会受到存储空间不足或权限问题的影响,所以本文将详细介绍如何... 目录引言一、为什么要修改 pip 的临时目录?1. 解决存储空间不足的问题2. 解决权限问题3. 提

C++原地删除有序数组重复项的N种方法

《C++原地删除有序数组重复项的N种方法》给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度,不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(... 目录一、问题二、问题分析三、算法实现四、问题变体:最多保留两次五、分析和代码实现5.1、问题分析5.

Linux文件名修改方法大全

《Linux文件名修改方法大全》在Linux系统中,文件名修改是一个常见且重要的操作,文件名修改可以更好地管理文件和文件夹,使其更具可读性和有序性,本文将介绍三种在Linux系统下常用的文件名修改方法... 目录一、引言二、使用mv命令修改文件名三、使用rename命令修改文件名四、mv命令和rename命

mybatis-plus 实现查询表名动态修改的示例代码

《mybatis-plus实现查询表名动态修改的示例代码》通过MyBatis-Plus实现表名的动态替换,根据配置或入参选择不同的表,本文主要介绍了mybatis-plus实现查询表名动态修改的示... 目录实现数据库初始化依赖包配置读取类设置 myBATis-plus 插件测试通过 mybatis-plu