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

【Day 11】238.除自身以外数组的乘积

文章目录

  • 238.除自身以外数组的乘积
  • 题目:
  • 思路:
  • 举例:
    • 方法一(原始思路,需要 L 和 R 两个数组)
    • 方法二(优化后,只用一个数组 + 一个变量 R)
      • 第一步:计算左边乘积 L,存到 ans
      • 第二步:再从右往左乘上右边积
  • 代码实现(Go):
    • 优化前:
    • 优化后:

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

题目:

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

请不要使用除法,且在 O(n) 时间复杂度内完成此题。

示例 1:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

提示:

2 <= nums.length <= 105
-30 <= nums[i] <= 30
输入 保证 数组 answer[i] 在 32 位 整数范围内


思路:

利用索引左侧所有数字的乘积和右侧所有数字的乘积(即前缀积与后缀积)相乘得到答案。

  1. 初始化两个空数组 L R。对于给定索引 i,L[i] 代表的是 i 左侧所有数字的乘积R[i] 代表的是 i 右侧所有数字的乘积
  2. 需要用两个循环来填充 L 和 R 数组的值。
    对于数组 L,L[0] 应该是 1,因为第一个元素的左边没有元素
    对于其他元素:L[i] = L[i-1] * nums[i-1]

当前位置 i 的左边积,等于它前一个位置的左边积,再乘上前一个位置的数。

  1. 同理,对于数组 R,R[length-1] 应为 1。length 指的是输入数组的大小。
    其他元素:R[i] = R[i+1] * nums[i+1]

当前位置 i 的右边积,等于它后一个位置的右边积,再乘上后一个位置的数。

  1. 当 R 和 L 数组填充完成,只需要在输入数组上迭代,且索引 i 处的值为:L[i] * R[i]

缺点:需要 O(n) 额外空间存储 L 和 R。

优化:只使用一个结果数组 ans,且不存储R数组,空间 O(1)。

L[i] 必须从左往右计算,所以可以先一边遍历一边计算好,放到 ans 里。(前缀积)
R[i] 必须从右往左计算,但其实不需要数组存下来,只要用一个变量 R 滚动维护右边乘积即可。(后缀积)

  1. 第一次从左往右遍历算出所有 L[i] 并存到 ans 数组里
    此时 ans[i] = L[i]
  2. 再从右往左遍历用一个变量 R 维护右边的乘积,不断更新
    每次让 ans[i] *= R(即在已有的左边积上再乘上右边积)。
    然后更新 R *= nums[i](让 R 包含当前位置的数,为下一步准备)。

举例:

假设:输入数组 nums = [1, 2, 3, 4],

方法一(原始思路,需要 L 和 R 两个数组)

  1. 先算左边乘积 L:
  • L[0] = 1(因为 0 左边没有数)
  • L[1] = L[0] * nums[0] = 1 * 1 = 1
  • L[2] = L[1] * nums[1] = 1 * 2 = 2
  • L[3] = L[2] * nums[2] = 2 * 3 = 6
    → L = [1, 1, 2, 6]
  1. 再算右边乘积 R:
  • R[3] = 1(因为最后一个数右边没有数)
  • R[2] = R[3] * nums[3] = 1 * 4 = 4
  • R[1] = R[2] * nums[2] = 4 * 3 = 12
  • R[0] = R[1] * nums[1] = 12 * 2 = 24
    → R = [24, 12, 4, 1]
  1. 最终结果 ans:
  • ans[i] = L[i] * R[i]
    → ans = [24, 12, 8, 6]

方法二(优化后,只用一个数组 + 一个变量 R)

第一步:计算左边乘积 L,存到 ans

  • 初始化 ans[0] = 1
  • ans[1] = ans[0] * nums[0] = 1 * 1 = 1
  • ans[2] = ans[1] * nums[1] = 1 * 2 = 2
  • ans[3] = ans[2] * nums[2] = 2 * 3 = 6
    → 此时 ans = [1, 1, 2, 6](其实就是 L)

第二步:再从右往左乘上右边积

  • 初始化 R = 1
  • i=3:ans[3] = ans[3] * R = 6 * 1 = 6;更新 R = R * nums[3] = 1 * 4 = 4
  • i=2:ans[2] = ans[2] * R = 2 * 4 = 8;更新 R = 4 * 3 = 12
  • i=1:ans[1] = ans[1] * R = 1 * 12 = 12;更新 R = 12 * 2 = 24
  • i=0:ans[0] = ans[0] * R = 1 * 24 = 24;更新 R = 24 * 1 = 24(用不到了)
    → 最终 ans = [24, 12, 8, 6]

代码实现(Go):

详细注解:

优化前:

// package main// import "fmt"func productExceptSelf(nums []int) []int {n := len(nums)L := make([]int, n) // L[i]: i 左侧所有元素的乘积R := make([]int, n) // R[i]: i 右侧所有元素的乘积ans := make([]int, n)// 计算左侧乘积 LL[0] = 1for i := 1; i < n; i++ {L[i] = L[i-1] * nums[i-1]}// 计算右侧乘积 RR[n-1] = 1for i := n - 2; i >= 0; i-- {R[i] = R[i+1] * nums[i+1]}// 组合答案:ans[i] = 左乘积 * 右乘积for i := 0; i < n; i++ {ans[i] = L[i] * R[i]}return ans
}// func main() {
// 	fmt.Println(productExceptSelf([]int{1, 2, 3, 4})) // 输出[24 12 8 6]
// }

优化后:

// package main// import "fmt"func productExceptSelf(nums []int) []int {n := len(nums)ans := make([]int, n) // 结果数组(通常不计入额外空间)// 1) 先写入“左边乘积”:ans[i] = nums[0..i-1] 的乘积// 最左边 i=0 的左侧没有元素,乘法单位元为 1ans[0] = 1for i := 1; i < n; i++ {// ans[i-1] 已经是 nums[0..i-2] 的乘积,// 乘上 nums[i-1] 就得到 nums[0..i-1] 的乘积ans[i] = ans[i-1] * nums[i-1]}// 2) 再从右往左,用滚动变量 R 记录“右边乘积”// 初始时最右端右侧无元素,因此取 1R := 1for i := n - 1; i >= 0; i-- {// 循环从 i = n-1(最后一个位置)开始// 当前 ans[i](左边乘积)乘上右边乘积 R,即为最终答案ans[i] = ans[i] * R// 把当前元素并入右边乘积,供左边的下一个 i-1 使用R *= nums[i]}return ans
}// func main() {
// 	fmt.Println(productExceptSelf([]int{1, 2, 3, 4})) // 输出[24 12 8 6]
// }

无注释(优化后):

package mainimport "fmt"func productExceptSelf(nums []int) []int {n := len(nums)ans := make([]int, n)ans[0] = 1for i := 1; i < n; i++ {ans[i] = ans[i-1] * nums[i-1]}R := 1for i := n - 1; i >= 0; i-- {ans[i] *= RR *= nums[i]}return ans
}func main() {fmt.Println(productExceptSelf([]int{1, 2, 3, 4})) // 输出[24 12 8 6]
}

http://www.xdnf.cn/news/1352629.html

相关文章:

  • Transformer核心概念I-token
  • SpringBoot 快速上手:从环境搭建到 HelloWorld 实战
  • Excel 条件高亮工具,秒高亮显示符合筛选条件的行数据
  • 「数据获取」《中国能源统计年鉴》(1986-2023)(获取方式看绑定的资源)
  • 蓝桥杯算法之基础知识(2)——Python赛道
  • 【51单片机学习】直流电机驱动(PWM)、AD/DA、红外遥控(外部中断)
  • mmdetection:记录算法训练配置文件
  • A Large Scale Synthetic Graph Dataset Generation Framework的学习笔记
  • Mysql EXPLAIN详解:从底层原理到性能优化实战
  • 如何在Ubuntu中删除或修改已有的IP地址设置?
  • C语言---数据类型
  • PyTorch生成式人工智能——VQ-VAE详解与实现
  • TypeScript 的泛型(Generics)作用理解
  • Kafka 概念与概述
  • 在TencentOS3上部署OpenTenBase:从入门到实战的完整指南
  • 【Java学习笔记】18.反射与注解的应用
  • 遥感机器学习入门实战教程|Sklearn案例⑧:评估指标(metrics)全解析
  • tcpdump命令打印抓包信息
  • 【golang】ORM框架操作数据库
  • 2-5.Python 编码基础 - 键盘输入
  • STM32CubeIDE V1.9.0下载资源链接
  • 醋酸镨:催化剂领域的璀璨新星
  • LangChain4J-基础(整合Spring、RAG、MCP、向量数据库、提示词、流式输出)
  • 信贷模型域——信贷获客模型(获客模型)
  • 温度对直线导轨的性能有哪些影响?
  • 小白向:Obsidian(Markdown语法学习)快速入门完全指南:从零开始构建你的第二大脑(免费好用的笔记软件的知识管理系统)、黑曜石笔记
  • 数字经济、全球化与5G催生域名新价值的逻辑与实践路径
  • 快速掌握Java非线性数据结构:树(二叉树、平衡二叉树、多路平衡树)、堆、图【算法必备】
  • vue3 - 组件间的传值
  • 【小沐学GIS】基于Godot绘制三维数字地球Earth(Godot)