当前位置: 首页 > news >正文

数组区域检索的优化 --- 分块,线段树,树状数组

思考


首先让我们来思考一个问题,给定一个数组,和left与right的值,让你求这个数组中left到right之间元素的和,你会怎么计算?最简单的当然是遍历。如果有人问你这个问题的时候,他决对是会让你优化的,起码时间复杂度一定要小于O(n),那你打算怎么做呢?


很明确的一点是,如果要优化时间复杂度,就必须要提高空间复杂度,这是算法的局限,当然也是自然界的能量守恒定律。这是不可避免的,


所以接下来你可以思考下,有什么结构可以实现了。

分块处理

这种就真的很基础了,就是开辟一个额外的数组来存储区间和。

设数组大小为 n,我们将数组 nums分成多个块,每个块大小 size,最后一个块的大小为剩余的不超过 size 的元素数目,那么块的总数为 n/size,用一个数组 sum 保存每个块的元素和。

那么究竟要分成多少个数组,才能在数量和速度上都是最优解那? 这个关键在于size的取值。在求和时:设left位于b1个块内的第i1个元素,right位于b2个块内的第i2个元素。如果b1=b2,那么直接返回第b1个块位于区间[i1,i2]的元素的和;否则计算第b1个块位于区间[i,size)的元素之和sum1,第b2个块位于区间[0,i2]的元素之和sum2,第b1+1个块到第b2-1个块的元素的总和sum3,返回sum1+sum2+sum3。这个求和的时间复杂度为O(size+\frac{n}{size})。因为size+\frac{n}{size} \geqslant 2\sqrt[]{n},当且仅当size = {\sqrt[]{n}}时等号成立。因此size的取值为\sqrt[]{n}。此时的时间复杂度为O(\sqrt[]{n})

var NumArray = function(nums) {this.nums = nums;const n = nums.length;size = Math.floor(Math.sqrt(n));this.sum = new Array(Math.floor((n + size - 1) / size)).fill(0); // n/size 向上取整for (let i = 0; i < n; i++) {this.sum[Math.floor(i / size)] += nums[i];}
};NumArray.prototype.update = function(index, val) {this.sum[Math.floor(index / size)] += val - this.nums[index];this.nums[index] = val;
};NumArray.prototype.sumRange = function(left, right) {const b1 = Math.floor(left / size), i1 = left % size, b2 = Math.floor(right / size), i2 = right % size;if (b1 === b2) { // 区间 [left, right] 在同一块中let sum = 0;for (let j = i1; j <= i2; j++) {sum += this.nums[b1 * size + j];}return sum;}let sum1 = 0;for (let j = i1; j < size; j++) {sum1 += this.nums[b1 * size + j];}let sum2 = 0;for (let j = 0; j <= i2; j++) {sum2 += this.nums[b2 * size + j];}let sum3 = 0;for (let j = b1 + 1; j < b2; j++) {sum3 += this.sum[j];}return sum1 + sum2 + sum3;
};

线段树

线段树是一种二叉树,也就是对于一个线段,我们会用一个二叉树来表示。比如说一个长度为4的线段,我们可以表示成这样:

这是什么意思呢? 如果你要表示线段的和,那么最上面的根节点的权值表示的是这个线段 1 ∼ 4  的和。根的两个儿子分别表示这个线段中 1 ∼ 2 的和,与 3 ∼ 4 的和。以此类推。

然后我们还可以的到一个性质:节点 i 的权值 = 她的左儿子权值 + 她的右儿子权值。因为 1 ∼ 4  的和就是等于 1 ∼ 2 的和与 2 ∼ 3 的和的和。

给定区间 [left,right]时,我们将区间 [left,right] 拆成多个结点对应的区间。

  • 如果结点 node 对应的区间与 [left,right] 相同,可以直接返回该结点的值,即当前区间和。
  • 如果结点 node 对应的区间与 [left,right]不同,设左子结点对应的区间的右端点为 m,那么将区间 [left,right] 沿点 m拆成两个区间,分别计算左子结点和右子结点。

我们从根结点开始递归地拆分区间 [left,right]。

var NumArray = function(nums) {n = nums.length;this.segmentTree = new Array(nums.length * 4).fill(0);this.build(0, 0, n - 1, nums);
};NumArray.prototype.update = function(index, val) {this.change(index, val, 0, 0, n - 1);
};NumArray.prototype.sumRange = function(left, right) {return this.range(left, right, 0, 0, n - 1);
};NumArray.prototype.build = function(node, s, e, nums) {if (s === e) {this.segmentTree[node] = nums[s];return;}const m = s + Math.floor((e - s) / 2);this.build(node * 2 + 1, s, m, nums);this.build(node * 2 + 2, m + 1, e, nums);this.segmentTree[node] = this.segmentTree[node * 2 + 1] + this.segmentTree[node * 2 + 2];
}NumArray.prototype.change = function(index, val, node, s, e) {if (s === e) {this.segmentTree[node] = val;return;}const m = s + Math.floor((e - s) / 2);if (index <= m) {this.change(index, val, node * 2 + 1, s, m);} else {this.change(index, val, node * 2 + 2, m + 1, e);}this.segmentTree[node] = this.segmentTree[node * 2 + 1] + this.segmentTree[node * 2 + 2];
}NumArray.prototype.range = function(left, right, node, s, e) {if (left === s && right === e) {return this.segmentTree[node];}const m = s + Math.floor((e - s) / 2);if (right <= m) {return this.range(left, right, node * 2 + 1, s, m);} else if (left > m) {return this.range(left, right, node * 2 + 2, m + 1, e);} else {return this.range(left, m, node * 2 + 1, s, m) + this.range(m + 1, right, node * 2 + 2, m + 1, e);}
}

树状数组

树状数组是一种可以动态维护序列前缀和的数据结构(序列下标从 1 开始)

如图,A是基本数组,C是求和数组,

其中,C[1]=A[1], C[2]=C[1]+A[2], C[3]=A[3], C[4]=C[2]+C[3]+A[4]......C[8]=C[4]+C[6]+C[7]+A[8]......

树状数组最简单最经典的使用场景,就是单点更新区间查询:

  • 单点修改 add(index,val):把序列第 index 个数增加 val;
  • 区间查询 prefixSum(index):查询前 index个元素的前缀和。

前置知识—lowbit(x)运算
如何计算一个非负整数n在二进制下的最低为1及其后面的0构成的数?
例如:44 的二进制表示为 (101100),最低为1和后面的0构成的数是( 100 ) 是 4 。

44的二进制=(101100),我们对44的二进制数取反+1,也即~44+1,得到-44

-44的二进制=(010100),然后我们把44和-44的二进制进行按位与运算,也即按位&得到,二进制000100,也就是十进制的4

所以lowbit(x) = x&(-x)

var NumArray = function(nums) {this.tree = new Array(nums.length + 1).fill(0);this.nums = nums;for (let i = 0; i < nums.length; i++) {this.add(i + 1, nums[i]);}
};NumArray.prototype.update = function(index, val) {this.add(index + 1, val - this.nums[index]);this.nums[index] = val;
};NumArray.prototype.sumRange = function(left, right) {return this.prefixSum(right + 1) - this.prefixSum(left);
};NumArray.prototype.lowBit = function(x) {return x & -x;
}NumArray.prototype.add = function(index, val) {while (index < this.tree.length) {this.tree[index] += val;index += this.lowBit(index);}
}NumArray.prototype.prefixSum = function(index) {let sum = 0;while (index > 0) {sum += this.tree[index];index -= this.lowBit(index);}return sum;
}

树状数组和线段树的区别在哪


有人会问了既然线段树的问题能够用树状数组解决而且线段树还比树状数组扩展性强,那为什么不直接用线段树呢?问的很好,树状数组的作用就是为了简化线段树,举个例子:一个问题可以用线段树解决写代码半个小时,但是用树状数组只需要10分钟,那么你会选择哪一个算法呢?没错,基于某些简单的问题,我们没必要用到功能性强但实现复杂的线段树(杀鸡焉用宰牛刀)。当并不是所有能用线段树解决的问题,用树状数组都可以解决。
 

相关文章:

数组区域检索的优化 --- 分块,线段树,树状数组

思考 首先让我们来思考一个问题&#xff0c;给定一个数组&#xff0c;和left与right的值&#xff0c;让你求这个数组中left到right之间元素的和&#xff0c;你会怎么计算&#xff1f;最简单的当然是遍历。如果有人问你这个问题的时候&#xff0c;他决对是会让你优化的&#xff…...

若依侧边栏添加计数标记效果

2023.11.13今天我学习了如何对若依的侧边栏添加技术标记的效果&#xff0c;如图&#xff1a; 我们需要用到两个页面&#xff1a; 先说子组件实现计数标记效果 1.item.vue <script> export default {name: MenuItem,functional: true,props: {icon: {type: String,defau…...

WebSocket技术解析:实现Web实时双向通信的利器

当今互联网的发展中&#xff0c;实时性成为了越来越重要的需求&#xff0c;特别是在Web应用程序中。传统的HTTP协议在处理实时性方面存在一些局限性&#xff0c;因此WebSocket技术的出现填补了这一空白。WebSocket是一种在单个TCP连接上进行全双工通信的协议&#xff0c;它允许…...

深圳联强优创手持PDA身份证阅读器 身份证核验手持机

身份证手持机外观比较小巧&#xff0c;方便携带&#xff0c;支持条码识别、人脸识别、NFC卡刷卡、内置二代证加密模块&#xff0c;可离线采集和识别二代身份证&#xff0c;进行身份识别。信息读取更便捷、安全高效。采用IP65高防护等级&#xff0c;1.5M防摔&#xff0c;可以适应…...

力扣labuladong——一刷day31

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣226. 翻转二叉树二、力扣116. 填充每个节点的下一个右侧节点指针三、力扣114. 二叉树展开为链表 二叉树解题的思维模式分两类&#xff1a; 1、是否可以…...

里氏代换原则

package com.jmj.principles.dmeo2.after;/*** 四边形接口*/ public interface Quadrilateral {double getLength();double getWidth();}package com.jmj.principles.dmeo2.after;/*** 长方形类*/ public class Rectangle implements Quadrilateral{private double length;priv…...

Illumination Adaptive Transformer

Abstract. 现实世界中具有挑战性的照明条件&#xff08;低光、曝光不足和曝光过度&#xff09;不仅会产生令人不快的视觉外观&#xff0c;还会影响计算机视觉任务。现有的光自适应方法通常单独处理每种情况。更重要的是&#xff0c;它们中的大多数经常在 RAW 图像上运行或过度…...

【教3妹学编程-算法题】给小朋友们分糖果 II

3妹&#xff1a;1 8得8&#xff0c;2 816&#xff0c; 3 8妇女节… 2哥 : 3妹&#xff0c;在干嘛呢 3妹&#xff1a;双11不是过了嘛&#xff0c; 我看看我这个双十一买了多少钱&#xff0c; 省了多少钱。 2哥 : 我可是一分钱没买。 3妹&#xff1a;我买了不少东西&#xff0c; …...

应急响应练习2

目录 1. 请提交攻击者的ip与系统版本 2. 攻击者通过某个组件漏洞获得服务器权限&#xff0c;请提交该组件的名称 3. 请提交攻击者首次攻击成功的时间 4. 请提交攻击者上传的webshell文件绝对路径 5. 请提交攻击者使用的webshell管理工具 6. 攻击者进一步留下的免杀的webs…...

JS算法练习 11.13

leetcode 2625 扁平化嵌套数组 请你编写一个函数&#xff0c;它接收一个 多维数组 arr 和它的深度 n &#xff0c;并返回该数组的 扁平化 后的结果。 多维数组 是一种包含整数或其他 多维数组 的递归数据结构。 数组 扁平化 是对数组的一种操作&#xff0c;定义是将原数组部…...

js升序排序

function sortByKey(array, key) {return array.sort(function(a, b) {var x Number(a[key]);var y Number(b[key]);return x < y ? -1 : x > y ? 1 : 0; //或者 return x-y});}...

2023亚太杯数学建模C题思路

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…...

【ArcGIS Pro微课1000例】0030:ArcGIS Pro中自带晕渲地貌工具的妙用

在ArcGIS中,制作地貌晕渲效果通常的做法是先制作山体阴影效果,然后叠加在DEM的下面,再改变DEM的透明度来实现。而在ArcGIS Pro中自带了效果显著的晕渲地貌工具。 文章目录 一、晕渲地貌工具1. 符号系统2. 栅格函数二、山体阴影效果1. 工具箱2. 栅格函数打开ArcGIS Pro3.0,加…...

【原创】java+swing+mysql办公用品管理系统设计与实现

摘要&#xff1a; 办公用品管理系统是一个设计和实现办公用品库存和使用管理的信息系统。此系统可以提高办公用品的利用率&#xff0c;减少浪费&#xff0c;使办公用品管理更加高效、规范、便捷。本文主要介绍使用javaswingmysql技术去开发实现一个办公用品管理系统。 功能分…...

sqlalchemy查询数据为空,查询范围对应的数据在数据库真实存在

记录一个开发过程遇到的小bug,构造些伪数据还原并解释。 """ 场景:传参触发了查询条件,数据库中是存在传参对应范围的数据,但是通过查询条件得到的查询结果为空 """ 入参场景一: start_time = "2023-11-13" end_time = "202…...

Code Former安装及使用

Code Former是南洋理工大学和商汤科技联合研究中心联合开发一款AI人脸修复算法&#xff0c;通过该算法&#xff0c;可以对已经模糊的图片进行人脸修复&#xff0c;找回斑驳的记忆 由于网上对于Code Former的封装&#xff0c;全都是要花钱&#xff0c;或者需要其他什么曲折的方式…...

SpringMVC--@RequestMapping注解

RequestMapping注解 RequestMapping注解的功能RequestMapping注解的位置RequestMapping注解的属性1、value属性2、method属性3、params属性&#xff08;了解&#xff09; 补充RequestParamRequestHeaderRequestBody RequestBody获取json格式的请求参数 ResponseBodyRestControl…...

ARM寄存器及功能介绍/R0-R15寄存器

1、ARM 寄存器组介绍 ARM 处理器一般共有 37 个寄存器&#xff0c;其中包括&#xff1a; &#xff08;1&#xff09; 31 个通用寄存器&#xff0c;包括 PC&#xff08;程序计数器&#xff09;在内&#xff0c;都是 32 位的寄存器。 &#xff08;2&#xff09; 6 个状态寄存器…...

js删除json数据中指定元素

delete 删除数组方法&#xff1a; function removeJSONRows() {var tab {"dataRows": [{"id": 1,"name": "使用部门"},{"id": 2,"name": "车辆走行路线"},{"id": 3,"name": &quo…...

广州华锐互动:VR刑侦现场执法实训助力警察全面提升警务能力

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术在多个领域开始得到广泛应用&#xff0c;其中包括公安执法培训。VR刑侦现场执法实训系统是一种采用虚拟现实技术&#xff0c;为公安执法人员提供模拟真实环境的培训工具。通过这种平台&#xff0c;公安人…...

Granite TimeSeries FlowState R1赋能网络安全:异常流量检测与预测

Granite TimeSeries FlowState R1赋能网络安全&#xff1a;异常流量检测与预测 最近和几个做运维和安全的朋友聊天&#xff0c;大家普遍有个头疼的问题&#xff1a;面对海量的网络流量数据&#xff0c;怎么才能提前发现那些“不对劲”的苗头&#xff1f;等攻击真的发生了&…...

Ubuntu 20.04 下 CasADi C++ 源码编译与机器人控制实战

1. 环境准备与依赖安装 在Ubuntu 20.04上编译CasADi C源码前&#xff0c;我们需要先搭建好开发环境。这个环节就像盖房子前要打好地基&#xff0c;缺一不可。我建议先更新系统软件包列表&#xff0c;避免后续出现版本冲突&#xff1a; sudo apt update && sudo apt u…...

SYNBO 已上线 BitMart 交易所,Synbo Camp 同步开启

2026年3月31日&#xff0c;Synbo.io 原生代币 SYNBO 将上线 BitMart 交易所&#xff0c;这也成为 Synbo 发展进程中的又一里程碑&#xff0c;并同步开启 Synbo Camp 招募活动。这不仅是一次产品上线与活动发布&#xff0c;更标志着 Synbo 正式向行业递交一套关于未来融资协作方…...

Leather Dress Collection效果展示:12款皮革服饰LoRA高清生成作品集

Leather Dress Collection效果展示&#xff1a;12款皮革服饰LoRA高清生成作品集 1. 项目介绍 Leather Dress Collection 是一个基于Stable Diffusion 1.5的LoRA模型集合&#xff0c;专门用于生成各种皮革服装风格的图像。这个系列包含了12种不同风格的皮革服饰模型&#xff0…...

3步解锁AI编程助手全部潜力:Cursor Pro功能优化工具深度解析

3步解锁AI编程助手全部潜力&#xff1a;Cursor Pro功能优化工具深度解析 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached yo…...

万象视界灵坛从零开始:开源多模态平台GPU算力适配与显存调优指南

万象视界灵坛从零开始&#xff1a;开源多模态平台GPU算力适配与显存调优指南 1. 平台概述与核心价值 万象视界灵坛是一款基于OpenAI CLIP模型的高级多模态智能感知平台&#xff0c;它将复杂的语义对齐任务转化为直观的像素风格交互体验。平台采用CLIP-ViT-L/14作为核心模型&a…...

seo泛站群的合法性问题如何避免_seo泛站群的运营团队应该怎样组建

SEO泛站群的合法性问题如何避免 在当前的互联网市场中&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;是一个重要的营销手段&#xff0c;其中泛站群&#xff08;SEO泛站群&#xff09;作为一种策略被广泛使用。泛站群的合法性问题和操作风险也随之而来。本文将深入探讨如…...

WebGPU实战指南:从零构建浏览器端高性能图形应用

1. WebGPU入门&#xff1a;为什么它是浏览器图形技术的未来 第一次接触WebGPU时&#xff0c;我被它的性能数据震惊了。在同样的硬件环境下&#xff0c;WebGPU渲染三角形的速度是WebGL的8-10倍。这就像从乡村小路突然切换到高速公路的感觉。你可能已经习惯了用Canvas或WebGL在浏…...

构建高效云点播系统(一):开源组件选型、安全防护与性能优化

1. 开源组件选型&#xff1a;从零搭建云点播系统的基石 第一次接触云点播系统时&#xff0c;我被各种开源组件的选择搞得晕头转向。经过几个项目的实战&#xff0c;我发现选对开源组件就像搭积木&#xff0c;基础打好了&#xff0c;后面的事情就水到渠成。这里分享几个我踩过坑…...

lingbot-depth-vitl14工业质检案例:玻璃瓶透明表面深度补全前后PSNR对比分析

lingbot-depth-vitl14工业质检案例&#xff1a;玻璃瓶透明表面深度补全前后PSNR对比分析 1. 引言&#xff1a;当工业质检遇上透明表面 在工业自动化生产线上&#xff0c;玻璃瓶、透明塑料件这类产品的质检一直是个头疼的问题。传统的视觉检测系统&#xff0c;面对透明或半透明…...