当前位置: 首页 > 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;公安人…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...