题目重点:输出数组保存左乘积,再累乘右乘积,解释常数辅助空间与精确整数边界。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 空间口径不计必须返回的输出数组;若把输出也算入,输出n项本身Ω(n)。
- 训练Python整数精确计算;固定宽度环境须确定溢出、模数或bigint政策。空积记1。
解题路线
- 第一遍out[i]记录所有左侧值乘积,prefix从1起,每次写出之后再乘当前值。第二遍右向左,suffix为严格右侧乘积,将out[i]乘suffix后再纳入当前值。
- 循环不变量表明out[i]=左积·右积恰好漏掉values[i]且包含其他项各一次,不需要除法。两个方向的更新顺序是保证排除自身的关键。
- 零自然被处理:一个零只会让零所在位置得到其他非零项乘积;两零时每个输出至少包含一个零。无须单独分支,但必须测这两类。
- log+exp是近似路线,负值需跟踪符号、零需特判、浮点误差可令整数结果错一单位甚至更多。若输出必须精确,用范围证明、检查乘法或大整数;不能把取log当无损溢出修复。
手动走一遍
训练例[2,0,3]→[0,6,0];[0,0,3]→[0,0,0];[-2,3,4]→[12,-8,-6];单元素[7]训练输出[1]。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def product_except_self(values):
"""Output space excluded from O(1) auxiliary space; Python integer arithmetic."""
out = [1] * len(values)
prefix = 1
for i,value in enumerate(values):
out[i] = prefix
prefix *= value
suffix = 1
for i in range(len(values)-1, -1, -1):
out[i] *= suffix
suffix *= values[i]
return out
复杂度
单位成本乘法模型O(n)时间、O(1)辅助空间、O(n)输出;大整数的位复杂度随积的位数增长,Python避免固定宽度溢出不等于每次乘法永远O(1)。
易错点与追问
容易失分的边界
- prefix先乘当前值再写出。
- 输出空间也计算却声称总O(1)。
- 用log/exp保证精确整数。
继续深挖
- 固定模数怎样改?
- 只允许固定宽度整数怎样证明范围?
- 动态点更新和单点除自身查询能否用线段树?
