
要解决“除自身以外数组的乘积”问题需在O(n) 时间复杂度 内完成计算且不使用除法避免除数为0等边界问题。核心思路是利用前缀积 和后缀积 的组合将时间复杂度优化至线性。步骤1理解问题本质给定整数数组nums需构造结果数组answer使得answer[i]等于nums中除nums[i]自身外所有元素的乘积。例如若nums [1,2,3,4]则answer[0] 2×3×4 24answer[1] 1×3×4 12依此类推。步骤2设计“前缀积 后缀积”策略直接暴力枚举每个元素、再遍历其余元素相乘时间复杂度 O(n²)会超时因此需用空间换时间前缀积数组ff[i]表示nums中[0, i-1] 区间的元素乘积即nums[i]左侧所有元素的乘积。后缀积数组gg[i]表示nums中[i1, n-1] 区间的元素乘积即nums[i]右侧所有元素的乘积。最终answer[i] f[i] × g[i]左侧乘积 × 右侧乘积恰好是“除自身外所有元素的乘积”。步骤3推导数组递推关系1前缀积数组f的构造初始化f[0] 1区间[0, -1]无元素乘积视为1。递推公式f[i] f[i-1] × nums[i-1]。解释f[i]是“前i个元素的乘积”需基于f[i-1]前i-1个元素的乘积再乘以第i-1个元素nums[i-1]。2后缀积数组g的构造初始化g[n-1] 1区间[n, n-1]无元素乘积视为1。递推公式g[i] g[i1] × nums[i1]。解释g[i]是“从i到末尾的元素乘积”需基于g[i1]从i1到末尾的乘积再乘以第i1个元素nums[i1]。步骤4代码实现以Java为例通过“前缀积 → 后缀积 → 结果合并”三步完成计算public int[] productExceptSelf(int[] nums) { int n nums.length; int[] f new int[n]; // 前缀积数组 int[] g new int[n]; // 后缀积数组 int[] answer new int[n]; // 1. 构造前缀积数组f f[0] 1; for (int i 1; i n; i) { f[i] f[i-1] * nums[i-1]; } // 2. 构造后缀积数组g g[n-1] 1; for (int i n-2; i 0; i--) { g[i] g[i1] * nums[i1]; } // 3. 计算结果前缀积 × 后缀积 for (int i 0; i n; i) { answer[i] f[i] * g[i]; } return answer; }步骤5复杂度分析时间复杂度O(n)。三次线性遍历构造前缀积、构造后缀积、合并结果每次遍历时间为 O(n)总时间为 O(3n) O(n)。空间复杂度O(n)前缀积数组f和后缀积数组g各占 O(n)若优化空间可将后缀积复用结果数组将空间复杂度降为 O(1)逻辑本质不变。进阶空间复杂度优化至 O(1)除结果数组外利用结果数组answer先存前缀积再用一个变量suffix动态维护后缀积直接在结果数组中计算最终值public int[] productExceptSelf(int[] nums) { int n nums.length; int[] answer new int[n]; // 1. 先将前缀积存入answer answer[0] 1; for (int i 1; i n; i) { answer[i] answer[i-1] * nums[i-1]; } // 2. 用变量suffix存后缀积从后往前更新answer int suffix 1; for (int i n-1; i 0; i--) { answer[i] answer[i] * suffix; // 前缀积 × 后缀积 suffix * nums[i]; // 更新后缀积下一个左元素需乘当前nums[i] } return answer; }核心思想总结通过前缀积覆盖“目标元素左侧所有元素的乘积”后缀积覆盖“目标元素右侧所有元素的乘积”两者相乘即为结果。这种方法避免了暴力枚举的高时间复杂度同时利用数组的线性遍历特性确保了效率与可读性。