【LeetCode】307 . 区域和检索 - 数组可修改

news2024/11/26 14:49:49

307 . 区域和检索 - 数组可修改

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

区间和解题思路

  • 这是一道很经典的题目,通常还能拓展出一大类问题。

    针对不同的题目,我们有不同的方案可以选择(假设我们有一个数组):

    • 数组不变,求区间和:「前缀和」、「树状数组」、「线段树」
    • 多次修改某个数(单点),求区间和:「树状数组」、「线段树」
    • 多次修改某个区间,输出最终结果:「差分
    • 多次修改某个区间,求区间和:「线段树」、「树状数组」(看修改区间范围大小)
    • 多次将某个区间变成同一个数,求区间和:「线段树」、「树状数组」(看修改区间范围大小)
  • 这样看来,「线段树」能解决的问题是最多的,那我们是不是无论什么情况都写「线段树」呢?

    答案并不是,而且恰好相反,只有在遇到第 4 类问题,不得不写「线段树」的时候,我们才考虑线段树。因为「线段树」代码很长,而且常数很大,实际表现不算很好。我们只有在不得不用的时候才考虑「线段树」

  • 总结一下,我们应该按这样的优先级进行考虑:

    • 简单求区间和,用「前缀和」
    • 多次将某个区间变成同一个数,用「线段树」
    • 其他情况,用「树状数组」

方法:树状数组

树状数组概述

  • 要介绍树状数组,首先引入二叉树:

    在这里插入图片描述

  • 变形一下,就得到了树状数组

    在这里插入图片描述

定义树状数组

  1. 首先定义一个累加和数组 sums,假设数组有 8 个元素,如图所示,其中 ni ​是原始数据, si 是累加和数据 :

    在这里插入图片描述

    s[1]=n[1];
    s[2]=n[1]+n[2];
    s[3]=n[3];
    s[4]=n[1]+n[2]+n[3]+n[4];
    s[5]=n[5];
    s[6]=n[5]+n[6];
    s[7]=n[7];
    s[8]=n[1]+n[2]+n[3]+n[4]+n[5]+n[6]+n[7]+n[8];
    
  2. 那么我们如何初始化 sums 这个数组呢?

    当我们要对 sums[1] 初始化时,也就是加上 nums[1],而 sums[2]、sums[4]、sums[8] 也都需要加上 nums[1]

    将这几个节点的下标写成二进制:sums[(001)]、sums[(010)]、sums[(100)]、sums[(1000)]

    不难发现,sums[(1000)] = sums[(100)] + 4sums[(100)] = sums[(010)] + 2sums[(010)] = sums[(001)] + 1,即可以表示成 sums[(y)] = sums[(x)] + C,C 就是 x 最低位数 1 代表的二进制,比如 x = 100 = 4 。

    对于上述发现,我们可以使用函数 lowbit(x) 来计算 x 最低位1 所代表的二进制:

    int lowbit(int x) {
        return x & (-x);
    }
    

    因此,sums[(y)] = sums[(x)] + lowbit(x)

    当我们对 sums 数组进行初始化的时候,我们就是将所有和 nums[i] 相关联的节点都加上 nums[i],不断向上操作,直到下标越界。

    void insert(int index, int val) {
        // 下标+1
        int x = index + 1;
        while (x < sums.size()) {
            sums[x] += val;
            x += lowbit(x); 
        }
    }
    

更新树状数组

  • 更新操作和初始化类似,都是执行向上操作,使用 x += lowbit(x)来寻找被影响的数组下标。

    在这里插入图片描述

    在这里插入图片描述

    void update(int index, int val) {
        int x = index + 1;
        while(x < sums.size()) {
            // 减去原先的值,加上新值
            sums[x] = sums[x] - nums[index] + val;
            x += lowbit(x);
        }
        nums[index] = val;
    }
    

查询树状数组

  • 查询是一个向下访问的过程,使用 x -= lowbit(x)来寻找下一个下标。

    在这里插入图片描述

    int query(int x) {
        int ans = 0;
        while(x > 0) {
            ans += sums[x];
            x -= lowbit(x);
        }
        return ans;
    }
    

区间求和

  • 在完成左右端点的前缀和查询后,就可以对区间求和。

    int sumRange(int left, int right) {
        return query(right + 1) - query(left);
    }
    

代码

class NumArray {
public:
    vector<int> sums; // 累加和
    vector<int> nums;
    int lowbit(int x){
        return x & (-x);
    }
    // 原数组长度+1, 原因是计算lowbit时,使用下标0会进入死循环
    NumArray(vector<int>& nums) : nums(nums){
        sums.resize(nums.size() + 1);
        for(int i=0; i<nums.size(); ++i){
            // 初始化累加和数组
            update(i, nums[i]);
        }
    }
    void insert(int index, int val) {
        // 下标+1
        int x = index + 1;
        while (x < sums.size()) {
            sums[x] += val;
            x += lowbit(x); 
        }
    }
    void update(int index, int val) {
        int x = index + 1;
        while(x < sums.size()) {
            // 减去原先的值,加上新值
            sums[x] = sums[x] - nums[index] + val;
            x += lowbit(x);
        }
        nums[index] = val;
    }
    
    int sumRange(int left, int right) {
        return query(right + 1) - query(left);
    }
    int query(int x) {
        int ans = 0;
        while(x > 0) {
            ans += sums[x];
            x -= lowbit(x);
        }
        return ans;
    }
};

/**
 * 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);
 */

参考资料

  1. 树状数组详解

  2. [树状数组] 详解树状数组, 包含更新查询图解, 秒懂lowbit含义(JAVA 65ms, 68.6MB)

  3. 关于各类「区间和」问题如何选择解决方案(含模板)

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

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

相关文章

多线程(进阶)

一、常见的锁策略 1.1读写锁 多线程之间&#xff0c;数据的读取方之间不会产生线程安全问题&#xff0c;但数据的写入方互相之间以及和读者之间都需 要进行互斥。如果两种场景下都用同一个锁&#xff0c;就会产生极大的性能损耗。所以读写锁因此而产生。 读写锁&#xff08;r…

高通清库存芯片大降价,由于手机复苏不及预期

KlipC报道&#xff1a;8月14日&#xff0c;高通为刺激客户拉货意愿并加快出清库存&#xff0c;近期也开启了降价模式&#xff0c;锁定中低端 5G 手机芯片。 KlipC的合伙人Andi D指出高通在非苹中高端手机市场一直处于领先地位&#xff0c;因此本次降价聚焦在中低阶领域&#xf…

生成模型的三个挑战:成功部署或吓跑用户?

一、说明 目前在文本和图像领域中&#xff0c;统治江湖的有六大门派&#xff0c;他们是&#xff1a;OpenAI&#xff0c;Google&#xff0c;Microsoft&#xff0c;Midjounery&#xff0c;StabilityAI&#xff0c;CharecterAI. 每个人都在竞相为文本到文本&#xff0c;文本到图像…

万物识别RAM:图像识别模型,Zero-Shot超越有监督

文章目录 RAM的优势RAM的创新点总结与展望参考文献大语言模型(Large Language Models)已经给自然语言处理(NLP)领域带来了新的革命。在计算机视觉(CV)领域,Facebook近期推出的Segment Anything Model(SAM)工作,在视觉定位(Localization)任务上取得了令人振奋的结果…

怎么对视频进行压缩?

怎么对视频进行压缩&#xff1f;视频压缩&#xff0c;我们都知道是将视频文件进行压缩变小的过程&#xff0c;是我们日常办公中较为常用的手段。现如今&#xff0c;在视频技术不断发展与创新的基础上&#xff0c;视频分辨率也在不断提高&#xff0c;进而导致文件占有量也非常大…

vue-cli前端工程化——创建vue-cli工程 router版本的创建 目录结构 案例初步

目录 引出创建vue-cli前端工程vue-cli是什么自动构建创建vue-cli项目选择Vue的版本号 手动安装进行选择创建成功 手动创建router版多了一个router 运行测试bug解决 Vue项目结构main.jspackage.jsonvue.config.js Vue项目初步hello案例 总结 引出 1.vue-cli是啥&#xff0c;创建…

Docker中部署Nginx

1.Nginx部署需求 2.操作教程 3.实际步骤 把配置粘过来。

什么是伪类选择器?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 伪类选择器⭐ 一些常见的伪类选择器示例&#xff1a;:hover:active:focus:nth-child(n):first-child 和 :last-child ⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何…

深入了解 Rancher Desktop 设置

Rancher Desktop 设置的全面概述 Rancher Desktop 拥有方便、强大的功能&#xff0c;是最佳的开发者工具之一&#xff0c;也是在本地构建和部署 Kubernetes 的最快捷方式。 本文将介绍 Rancher Desktop 的功能和特性&#xff0c;以及 Rancher Desktop 作为容器管理平台和本地…

HTML5+CSS3查缺补漏

浏览器的渲染过程 JS加载执行 普通js/sync&#xff1a;阻塞 DOM加载解析 async&#xff1a;下载完就执行&#xff0c;无依赖 <script type"text/javascript" src"x.min.js" async"async"></script> defer&#xff1a;渲染完再执行…

【从零学习python 】19. 循环遍历列表和列表嵌套的应用

文章目录 列表的循环遍历1. 使用while循环2. 使用for循环3. 交换2个变量的值1. 列表嵌套2. 应用 进阶案例 列表的循环遍历 1. 使用while循环 为了更有效率的输出列表的每个数据&#xff0c;可以使用循环来完成 namesList [xiaoWang,xiaoZhang,xiaoHua] length len(namesLi…

【奶奶看了都会】2分钟学会制作最近特火的ikun幻术图

1.效果展示 最近ikun幻术图特别火啊&#xff0c;在网上能找到各种各样的ikun姿势图片&#xff0c;这些图片都是AI绘制的&#xff0c;能和风景完美融合在一起&#xff0c;今天小卷就来教大家怎么做这种图片 先看看图片效果 视频链接&#xff1a; 仿佛见到一位故人&#xff0c;…

安装 MySQL phpMyAdmin cpolar实现远程访问连接

文章目录 1. 安装Mysql2. 安装phpMyAdmin3. 修改User 表4. 本地测试连接5. 安装cpolar6. 配置公网访问地址7. 固定连接公网地址 群晖安装MySQL具有高效、安全、可靠、灵活等优势&#xff0c;可以为用户提供一个优秀的数据管理和分析环境。同时具有良好的硬件性能和稳定性&#…

牛客网华为OD前端岗位,面试题库练习记录01

题目一 质数因子 功能:输入一个正整数&#xff0c;按照从小到大的顺序输出它的所有质因子&#xff08;重复的也要列举&#xff09;&#xff08;如180的质因子为2 2 3 3 5 &#xff09; JavaScript Node ACM模式 const rl require("readline").createInterface({ i…

【需求输出】用户故事方法

文章目录 1、初识用户故事2、用户故事是描述需求的最好方式3、创建用户故事4、用户故事的分层管理5、编写用户故事的工具 1、初识用户故事 2、用户故事是描述需求的最好方式 3、创建用户故事 4、用户故事的分层管理 5、编写用户故事的工具

0101xss入门及pikachu靶场-xss-web安全-网络安全

文章目录 0 概述1 环境准备2 反射型xss2.1 概述2.1 靶场-反射型xss&#xff08;get&#xff09; 3 存储型xss3.1 概述3.2 靶场-存储型xss 4 DOM型xss4.1 概述4.2 靶场-DOM型xss 5 问题总结6.1 再次启动pikachu容器报错 结语 0 概述 学习路线&#xff0c;如如下图所示&#xff…

基于架构的软件开发方法

基于架构的软件开发方法 基于架构的软件开发方法是由架构驱动的&#xff0c;即指由构成体系结构的商业、质量和功能需求的组合驱动的。使用ABSD 方法&#xff0c;设计活动可以从项目总体功能框架明确就开始&#xff0c;这意味着需求抽取和分析还没有完成(甚至远远没有完成)&am…

ComponentOne Studio ASP.NET MVC Crack

ComponentOne Studio ASP.NET MVC Crack FlexReport增强功能 添加了对在Microsoft Windows上部署Microsoft Azure的支持。 添加了对显示嵌入字体的支持。 .NET标准版的经典C1PDF(Beta版) GrapeCity的经典C1Pdf库现在提供了基于Microsoft.NET标准的版本。在任何.NET应用程序(包括…

Linux常用有关目录的基本命令

一、目录管理 1、绝对路径和相对路径&#xff1a; 绝对路径&#xff1a; 路径的写法&#xff0c;由跟目录 / 写起&#xff0c;例如&#xff1a;/usr/share/doc这个目录。 相对路径&#xff1a; 路径的写法&#xff0c;不是由 / 写起&#xff0c;例如由 /usr/share/doc 要到 /u…

如何求解粘聚力和内摩擦角?

1.直接由莫尔应力圆及包络线得到 由于三轴条件下&#xff0c;试样以宏观破裂面破坏&#xff0c;所以试件是以剪切的方式破坏的&#xff0c;根据库伦强度准则&#xff0c;其抗剪强度 τ m \tau_m τm​ 可以通过粘聚力 c c c 和内摩擦角 φ \varphi φ 确定&#xff0c;表示为…