我爱学算法之—— 前缀和(中)

news2025/6/9 20:01:17

一、724. 寻找数组的中心下标

题目解析

在这里插入图片描述

这道题,给定数组nums,要求我们找出这个数组的中心下标。

**中心下标:**指左侧所有元素的和等于右侧所有元素的和。

如果存在多个中心数组下标,就返回最左侧的中心数组下标。

算法思路

暴力解法:

对于这道题,要找出数组的中心下标,暴力解法就是遍历数组,依次判断该位置中心下标(左侧所有元素等于右侧所有元素)。

对于暴力解法,遍历数组nums

遍历到i位置时,判断该位置是否是中心下标,也就是该位置左侧所有元素是否等于右侧所有元素。

优化:

暴力解法要遍历数组,遍历到i位置时还需求左侧所有元素的和、右侧所有元素的和,就还需再遍历数组来求。

时间复杂度就是O(n);对于遍历数组的每一个元素,依次判断该位置是否是中心下标,这里进行不了优化;

那就来看:遍历到i位置时,求左侧所有元素的和、右侧所有元素的和

在暴力解法中,我们就遍历i位置左侧的所有元素,求左侧所有元素的和;遍历i位置右侧所有元素的和,求右侧所有元素的和。

我们可不可以使用更简单的方法来拿到i位置所有元素的和?

当然是可以的,我们可以预先处理前缀和与后缀和数组,这样就可以以O(1)的时间复杂度拿到i位置左侧所有元素的和、i位置右侧所有元素的和。

前缀和:

  • 预处理前缀和、后缀和数组:遍历到i位置时需要i位置前面所有元素的和,i位置后面所有元素的和;这里预先处理前缀和数组f和后缀和数组g

    前缀和数组ff[i]表示区间[0,i-1]中所有元素的和。i位置前面所有元素的和)

    后缀和数组gg[i]表示区间[i+1 , n-1]中所有元素的和。i位置后面所有元素的和)

  • **使用前缀和、后缀和数组:**有了前缀和、后缀和数组,在遍历到i位置时只需判断f[i]是否等于g[i]即可。

    如果f[i] == g[i],就表示i位置是一个中心位置,返回该位置下标i即可。

    如果f[i] != g[i],就表示i位置不是应该中心位置,继续向后遍历即可。

预处理前缀和:

这里f[i]表示的是[0 , i-1]中所有元素的和,所以在填表时从1开始向后填表;f[i] = f[i-1] + nums[i];

g[i]表示的是[i+1 , n-1]中所有元素的和,所以在填表时从n-2开始向前填表;g[i] = g[i+1] + nums[i];

初始化:

  • 在填f[1]时会用到f[0],而f[0]表示的是[0,-1]中所有元素的和,该区间不存在。所以初始化f[0] = 0
  • 在填g[n-2]时会用到g[n-1],而g[n-1]表示[n,n-1]中所有元素的和,该区间不存在、所以初始化g[n-1] = 0

代码实现

class Solution {
public:
    int f[10002]; //[0 , i-1]
    int g[10002]; //[i+1 , n-1]
    int pivotIndex(vector<int>& nums) {
        int n = nums.size();
        f[0] = 0;
        g[n] = 0;
        for (int i = 1; i <= n; i++)
            f[i] = f[i - 1] + nums[i - 1];
        for (int i = n - 2; i >= 0; i--)
            g[i] = g[i + 1] + nums[i + 1];
        for (int i = 0; i < n; i++) {
            if (f[i] == g[i])
                return i;
        }
        return -1;
    }
};

二、238. 除自身以外数组的乘积

题目解析

在这里插入图片描述

对于这道题,给定一个数组nums,要求出数组answer

其中anwser[i]表示nums数组中除了nums[i]以外的所有数的乘积。

算法思路

对于这道题,要返回数组answer,其中answer[i]表示除了nums[i]以外的所有数的乘积。

那也就是说,我们需要求出nums中每一个元素对应的answer[i]

暴力解法:

遍历整个数组nums,遍历到i位置时,再遍历整个数组计算除了i位置之外其他所有数的乘积。

这里时间复杂度就是O(n^2)

优化

这里当我们遍历到i位置时,暴力解法就是再次遍历数组,来计算除了i以外的所有数的乘积。

也就是以O(n)的时间复杂度来获取除了i位置意外所有数的乘积。

这里我们可不可以通过预先处理,来直接就可以拿到除了i位置意外的所有数的乘积。

这里,当遍历到i位置时,我们可以发现i位置把整个数组分成了两部分:

一部分是区间[0 , i-1]、另一部分则是区间[i+1 , n-1]

那我们只要拿到区间[0 , i-1]中所有数的乘积,区间[i+1 , n-1]中所有数的乘积那就可以直接计算除了i位置以外所有数的乘积。

在这里插入图片描述

前缀和(积)

所以,我们就可以通过预先处理前缀积数组,在遍历到i位置时,就可以以O(1)的时间复杂度获取到除了i位置以外的所有数的积。

  • 前缀积数组:f[i]表示区间[0 , i-1]中所有数的积
  • 后缀积数组:g[i]表示区间[i+1 , n-1]中所有数的积

预处理前/后缀积数组:

  • 前缀积:f[i] = f[i-1] * nums[i-1]
  • 后缀积:g[i] = g[i+1] * nums[i+1]

使用前/后缀积数组:

在遍历到i位置时,除i位置以外的所有数的乘积sum = f[i] * g[i]。(这里就可以使用O(1)的时间复杂度获取除i位置以外的所有数的乘积)

初始化:

  • 在预处理前缀积数组时,f[1]会用到f[0],而区间[0,-1]显然不存在;为了不影响前缀积数组中的其他数,将f[0]初始化为1
  • 在预处理后缀积数组时,g[n-2]会用到g[n-1],而区间[n , n-1]显然不存在;为了不影响后缀积中的其他数,将g[n-1]初始化为1

细节:

  • 前缀积:前缀积数组f[i]表示区间[0 , i-1]中所有数的积,在预处理时要处理到n位置。(从前往后)
  • 后缀积:后缀积数组g[i]表示区间[i+1 , n-1]中所有数的积,在预处理时就要处理到0位置。(从后往前)

代码实现

class Solution {
public:
    int f[100001];
    int g[100001];
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        f[0] = g[n - 1] = 1;
        for (int i = 1; i <= n; i++) {
            f[i] = f[i - 1] * nums[i - 1];
        }
        for (int i = n - 2; i >= 0; i--) {
            g[i] = g[i + 1] * nums[i + 1];
        }
        vector<int> ret(n);
        for (int i = 0; i < n; i++) {
            ret[i] = f[i] * g[i];
        }
        return ret;
    }
};

三、560. 和为 K 的子数组

题目解析

在这里插入图片描述

对于这道题,给定一个数组nums和一个整数k

我们要求在nums数组中存在多少个和为k的子数组。

注意:这道题中给的的数据范围:-1000 <= nums[i] <= 1000

算法思路

暴力解法:

首先,对于这道题,暴力解法就是枚举:枚举所有的子数组,然后找出和为k的子数组的个数。

枚举所有子数组,以i为起始位置,j为结束位置的子数组;

求出子数组的和,然后找到子数组和为k的个数。

前缀和:

在暴力解法枚举所有子数组中,本质就是先固定i位置,再枚举以i位置为起始位置的子数组,寻找和为k 的子数组。

那我们也可以先固定i位置,再枚举所有以i位置为结束位置的子数组,然后在这些子数组中,找到和为k的子数组。

所以,我们在遍历数组时,就可以先固定i位置,再枚举以i位置为结束位置的所有子数组,在这些子数组中找和为k的子数组

那如何去找呢?

在遍历到i位置时,我们要找以i位置为结束位置的所有子数组这和为k的子数组的个数;

那我们就可以将问题转换以下:在区间[0 , i]中寻找前缀和为sum - k的个数。

在这里插入图片描述

所以,我们就可以在区间[0 , i-1]中找前缀和为sum - k的个数,找到的就是以i为结束位置、和为k的子数组的个数。

所以,现在我们预处理处一个前缀和数组,然后遍历nums数组,遍历到i位置时就要在区间[0 , i-1]中找前缀和等于sum - k的数量

但是这样来解的话,时间复杂度貌似还不如暴力解法啊,时间复杂度为O(n^2 + n)啊。

hash表优化:

我们知道了使用前缀和如何去求以i位置为结束位置、和为k的子数组的个数;但是时间复杂度却不如暴力解法。

原因就是:在遍历i位置时我们还需要去遍历区间[0 , i-1]中的所有前缀和,才能够知道前缀和为sum - k的数量。

这里要求前缀和为sum - k的数量,就可以使用hash表来优化:

在遍历到i位置时,hash表中存储区间[0 , i-1]中所有前缀和以及前缀和出现的次数。(这样我们就可以直接获取前缀和为sum - k的数量。

细节问题:

  • hash表中存储的是区间[0 , i-1]中所有的前缀和以及前缀和出现的次数;所以在遍历数组的过程中,依次统计前缀和出现的次数。

  • 初始情况下hash中一个存在前缀和为0,出现次数为1的元素:(0 , 1);如果整个数组的和为k,只有存在(0 , 1)才能统计上这种情况。

  • 使用一个数sum来代替整个前缀和数组;这里没有必要去预处理一个前缀和数组,只需用sum即可代替;

    sum表示遍历到i位置时,区间[0 , i]位置的和;这样i位置之前的前缀和都被统计在hash表中,而i位置后面的前缀和不需要。

在这里插入图片描述

代码实现

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int n = nums.size();
        unordered_map<int, int> hash;
        hash[0] = 1;
        int sum = 0, ret = 0;
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            if (hash.count(sum - k))
                ret += hash[sum - k];
            hash[sum]++;
        }
        return ret;
    }
};

到这里本篇文章内容就结束了,感谢各位的支持
继续加油!!!

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

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

相关文章

Elasticsearch从安装到实战、kibana安装以及自定义IK分词器/集成整合SpringBoot详细的教程ES(三)

DSL官方地址&#xff1a; DSL查询分类 Elasticsearch提供了基于JSON的DSL&#xff08;https://www.elastic.co/docs/explore-analyze/query-filter/languages/querydsl&#xff09;来定义查询。常见的查询类型包括&#xff1a; 查询所有&#xff1a;查询出所有数据&#xff0…

React Hooks 指南:何时使用 useEffect ?

在 React 的函数组件中&#xff0c;useEffect Hook 是一个强大且不可或缺的工具。它允许我们处理副作用 (side effects)——那些在组件渲染之外发生的操作。但是&#xff0c;什么时候才是使用 useEffect 的正确时机呢&#xff1f;让我们深入探讨一下&#xff01; 什么是副作用…

API标准的本质与演进:从 REST 架构到 AI 服务集成

在当今数字化浪潮中&#xff0c;API 已成为系统之间沟通与协作的“语言”&#xff0c;REST&#xff08;Representational State Transfer&#xff0c;表述性状态转移&#xff09;是一种基于 HTTP 协议的 Web 架构风格。它不仅改变了 Web 应用开发的方式&#xff0c;也成为构建现…

html - <mark>标签

<mark> 标签在HTML中用于高亮显示文本&#xff0c;通常用于突出显示某些重要的部分。它的默认样式通常是背景色为黄色&#xff0c;但你可以通过CSS自定义其外观。 1. 基本用法 <mark> 标签用于标记文本的高亮显示。它常用于搜索结果中&#xff0c;突出显示匹配的…

JavaWeb:前端工程化-Vue

Vue工程化 介绍 什么是Vue? 小白眼里前端开发 前端工程化 环境准备 D:\Program Files\nodejs Vue项目-快速入门 步骤 D:\front\vue 安装依赖 目录结构 code . vscode打开 启动 VScode侧边栏左下角&#xff0c;没有NPM脚本&#xff0c;如何打开&#xff1f;&…

AT_abc409_e [ABC409E] Pair Annihilation

AT_abc409_e [ABC409E] Pair Annihilation 赛时没开longlong挂了。 思路 首先我们可以把这棵树转化为一颗有根树&#xff0c;且所有电子的都朝根节点移动。 那么接下来我们就需要选择一个最优的树根。 考虑换根dp。 但是可以发现换根时答案其实是没有变化的。 我们设 f…

开疆智能Ethernet/IP转Modbus网关连接西门子BW500积算仪配置案例

本案例是通过Ethernet转Modbus网关将皮带秤数据接入到罗克韦尔1769L32E型PLC中。 首先进行ABB PLC的设置 1&#xff0c; 运行 RSLogix 5000 程序加载Ethernet转Modbus网关的EDS 文件&#xff1a; 2&#xff0c;新建工程并添加PLC 3&#xff0c;New Module添加网关&#xff…

【五子棋在线对战】三.数据管理模块实现

数据管理模块实现 1.数据库表的设计2.数据管理模块的封装和实现2.1 user_table() && ~user_table()2.2 insert() 注册时新增用户2.3 login() 登录验证&#xff0c;并返回详细的用户信息2.4 通过用户名获取用户信息 && 通过用户id获取用户信息2.5 win() &&a…

【JMeter】后置处理器 - 提取器

文章目录 概览边界提取器正则提取器JSON提取器 概览 CSS/JQuery提取器&#xff1b;给网页使用JSON提取器&#xff1a;给JSON数据使用★边界提取器&#xff1a;给字符串使用★正则表达式提取器&#xff1a;更加高级的字符使用★Xpath提取器&#xff1a;给网页使用 边界提取器…

OpenAI技术路线急转:从TypeScript到Rust的Codex CLI重构内幕

目录 前言&#xff1a;OpenAI的技术抉择引发业界思考 Codex CLI&#xff1a;OpenAI的终端AI编程利器 语言抉择的戏剧性反转&#xff1a;从TypeScript到Rust Rust重写的四大技术动因 1. 零依赖部署&#xff1a;消除环境配置痛点 2. 内存安全与沙箱隔离 3. 性能的全面碾压 …

window下配置ssh免密登录服务器

window下配置ssh免密登录服务器 本地windows远程登录我的ssh服务器10.10.101.xx服务器&#xff0c;想要每次都免密登录这个服务器. 记录下教程&#xff0c;防止后期忘记&#xff0c;指导我实现这个过程。 教程 二、实践步骤&#xff1a;Windows 上配置 SSH 免密登录 2.1 确…

nginx部署

配置阿里云yum源 安装如下编译工具 yum install -y gcc gcc-c autoconf automake make #安装使用nginx还得安装nginx所需的一些第三方系统库的支持&#xff0c;比如nginx的静态资源压缩功能所需的gzip lib库&#xff0c;nginx需要支持URL重写&#xff0c;所需的pcre库&…

线性规划饮食问题求解:FastAPI作为服务端+libhv作为客户端实现

之前在 Pyomo介绍-CSDN博客 中介绍过通过Pyomo求解线性规划问题&#xff0c;这里使用FastAPI作为服务端&#xff0c;开源网络库libhv作为客户端&#xff0c;求解饮食成本最小化问题。 服务端测试代码test_fastapi_pyomo_server.py如下&#xff1a; from fastapi import FastAP…

前端验证下跨域问题(npm验证)

文章目录 一、背景二、效果展示三、代码展示3.1&#xff09;index.html3.2&#xff09;package.json3.3&#xff09; service.js3.4&#xff09;service2.js 四、使用说明4.1&#xff09;安装依赖4.2&#xff09;启动服务器4.3&#xff09;访问前端页面 五、跨域解决方案说明六…

Linux Docker的简介

参考资料 30分钟Docker入门教程 ◀ 本篇博客所有图片皆来自于该视频截图阮一峰 - Docker 入门教程 目录 一. 环境配置时可能会遇到的问题二. 什么是Docker三. 虚拟机 与 Docker 的区别3.1 虚拟机3.2 Docker 四. Docker的基本架构五. Dockerfile 一. 环境配置时可能会遇到的问题…

极昆仑智慧与数元灵科技达成战略合作

近日&#xff0c;北京极昆仑智慧科技有限公司与北京数元灵科技有限公司正式签署产品级融合战略合作协议&#xff0c;双方将围绕 "AIBI商业智能分析" " Hybrid RAG 大模型问答" 等核心大模型应用&#xff0c;实现技术架构与业务场景的深度集成&#xff0c;…

第四讲:类和对象(下)

1. 再探构造函数 • 之前我们实现构造函数时&#xff0c;初始化成员变量主要使⽤函数体内赋值&#xff0c;构造函数初始化还有⼀种⽅ 式&#xff0c;就是初始化列表&#xff0c;初始化列表的使⽤⽅式是以⼀个冒号开始&#xff0c;接着是⼀个以逗号分隔的数据成 员列表&#xff…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Dad Jokes(冷笑话卡片)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— DadJokes 组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 豆包翻译确实可以&#xff0c;冷笑话应该属于各类语言比较难理解的…

Spring AOP执行原理源码解析

对【com.example.demo.TestAspect#aopTest】连接点增加了五个通知 在调用【com.example.demo.A#testAop()】&#xff08;用户自定义&#xff09;方法时&#xff0c;Cglib拦截器对其进行了拦截 可以看到执行顺序分别是环绕前置&#xff0c;前置&#xff0c;环绕后置&#xff0c;…

基于FPGA的超声波显示水位距离,通过蓝牙传输水位数据到手机,同时支持RAM存储水位数据,读取数据。

基于FPGA的超声波显示水位距离 前言一、整体框架二、代码架构1.超声波测距模块2.蓝牙数据发送模块3.数码管数据切换模块4.数码管驱动模块6.串口驱动7.顶层模块8.RAM ip核 仿真相关截图 前言 随着工业化进程的加速和环境保护意识的提升&#xff0c;对水资源管理和水位监测的需求…