构建乘积数组[前后缀&优化]

163 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第35天,构建乘积数组[前后缀&优化] - 掘金 (juejin.cn)

前言

前后缀是一种典型的空间换时间的做法,通过记录每个位置的状态来达到减少计算,而且当有前后遍历时,还可以用变量替代数组,因为前后缀是滚动的,就像是动态规划一样。

一、构建乘积数组

image.png

二、前后缀

1、前后缀直接相乘

// 构建乘积数组
public class ConstructArr {
    public int[] constructArr(int[] a) {
        /*
        计算每个位置的前后缀,每个位置的值 = 前缀 * 后缀。
        */
        int n = a.length;
        // 特殊情况,防止空数组。
        if (n == 0) return new int[]{};
        // 初始化前后缀。
        int[] prefix = new int[n];
        prefix[0] = 1;
        int[] suffix = new int[n];
        suffix[n - 1] = 1;
        // 为前后缀中间可同时计算的部分赋值。
        for (int i = 1; i < n - 1; i++) {
            prefix[i] = a[i - 1] * prefix[i - 1];
            suffix[n - i - 1] = a[n - i] * suffix[n - i];
        }
        // 为前后缀不可同时赋值的特殊位置赋值。
        prefix[n - 1] = prefix[n - 2] * a[n - 2];
        suffix[0] = suffix[1] * a[1];

        // 计算前后缀乘积。
        int[] r = new int[n];
        for (int i = 0; i < n; i++) r[i] = prefix[i] * suffix[i];
        // 返回计算结果。
        return r;
    }
}

2、状态压缩

// 改进:由于计算前后缀乘积时,需要往一个方向遍历,所以可少一个前/后缀。
class ConstructArr2 {
    public int[] constructArr(int[] a) {
        /*
        计算每个位置的前后缀,每个位置的值 = 前缀 * 后缀。
        */
        int n = a.length;
        // 特殊情况,防止空数组。
        if (n == 0) return new int[]{};
        // 从左往右遍历,可用prefix代替prefix[].
        int prefix = 1;
        // 初始化后缀。
        int[] suffix = new int[n];
        suffix[n - 1] = 1;
        // 为后缀每个位置赋值。
        for (int i = n - 2; i >= 0; i--) suffix[i] = a[i + 1] * suffix[i + 1];
        // 计算前后缀乘积。
        int[] r = new int[n];
        for (int i = 0; i < n; i++) {
            r[i] = prefix * suffix[i];
            // 更新前缀。
            prefix *= a[i];
        }
        // 返回计算结果。
        return r;
    }
}

总结

1)前后缀可以空间换时间,当结合遍历时,还可以进行空间压缩。

参考文献

[1] LeetCode 构建乘积数组