getMaxLength(arr, n) {
    if (arr.length <= 0) { return [] }
    let sum = [0];
    // 前 i 项和;
    // sum[i] = arr[0] + arr[1] + ... + arr[i-1]
    // 因此,arr [i, j) 之间的子数组和为 sum[j] - sum[i]

    // stk ,见下。
    let stk = [0];
    let s = 0;
    for (let i = 0; i < arr.length; i++) {
        // 生成 sum
        s += arr[i];
        sum[i + 1] = s;

        // 维护 stk :
        // 在将 i+1 加入 stk 前,移除 stk 中所有 sum[stk[.]] <= stum[stk[i+1]] 的元素
        let top = stk[stk.length - 1];
        while (stk.length > 0 && s <= sum[top]) {
            top = stk.pop();
        }
        stk.push(i + 1);
    }
    // 经过以上处理:
    // 1. sum[stk[i]] 严格单调增加
    //    对任何 i < j , sum[stk[i]] < sum[stk[j]]
    // 2. arr.length 在 stk 数组中
    // 3. 对任何 stk[i] < k < stk[i+1] ,sum[stk[i+1]] <= sum[k]
    //    证: (反证)
    //         若存在 stk[i] < k < stk[i+1], sum[stk[i+1]] > sum[k], 
    //         另其中使sum[k]最小的一个 k 中,最后一个为 k0 。
    //         于是,处理 k0 时,k0 被加入 stk (每一个都会先被被加入,stk.push 是无条件的)
    //         在处理任何 k0 < l <= stk[i+1] 时,由于 sum[l] > sum[k0] (假设条件),k0 不会被从 stk 中弹出,即 k0 最终将在 stk 中。
    //         这与 stk[i] < k0 < stk[i+1] 矛盾(k0 不在 stk 中)
    //         所以原假设不成立
    // 4. 若和最长的子数组为 [i, j) ,则 j 在 stk 中。
    //    证: (反证)
    //         如果 j 不在 stk 中,则存在 l ,使得 stk[l] > j (arr.length 在 stk 中)
    //         设最小的一个 l 为 l0 。
    //         如 l0 == 0 ,易知 sum[stk[l0]] = sum[stk[0]] <= sum[j] 。(证明与 3 类似)
    //         如果 l0 > 0 ,则 stk[l0-1] < j < stk[l0] ,依然有 sum[stk[l0]] <= sum[j]。
    //         于是 ,sum[stk[l0]] - sum[i] <= sum[j] - sum[i] < n,
    //         即 [i, stk[l0]) 也是一个符合条件的子数组。
    //         但该子数组比 [i, j) 要长,矛盾。
    //         所以原假设不成立。

    let start = 0;
    let end = 0;
    let max_len = 0;
    let max_s = 0;
    let max_e = 0;
    while (true) {
        // 求以 start 开始的最长连续子数组
        //    终点一定在 stk[] 中
        //    由于 sum[stk[]] 严格单调增加
        //    循环在 sum[stk[end]] - sum[start] >=n 后可终止
        // 注意 end 不是 arr 的下标,而是 stk 的下标
        while (end < stk.length && sum[stk[end]] - sum[start] < n) {
            if (stk[end] - start > max_len) {
                max_len = stk[end] - start;
                max_s = start;
                max_e = stk[end];
            }
            end++;
        }

        if (end === stk.length) {
            break;
        }

        // 如果以 start 开始的和小于 n 的最长连续子数组 为 [start, e = start + len)
        // 则对任何 s_new > start, 如果 sum[s_new] <= sum[start] ,
        // 其 和小于 n 的最长连续子数组长处不会超过 len 。
        // 证 : 否则,若 [s_new, e_new = s_new + len_new) 和小于 n ,且 len_new > len ,
        //     则, sum[e_new] - sum[s_new] < n
        //          sum[e_new] - sum[start] <= sum[e_new] - sum[s_new] < n
        //      而 e_new - start = s_new + len_new - start > len_new >= len 
        //      即 [start, e_new) 将是一个符合条件的数组且更长,
        //      与 [start, e) 是已 start 开始的符合条件的最长子数组矛盾
        // 所以,此处可以前移 start 至 sum[s_new] > sum[start]
        let s_new = start + 1;
        while (s_new < arr.length && sum[s_new] <= sum[start]) {
            s_new++;
        }
        start = s_new;
    }
    console.log(arr.slice(max_s, max_e))
    return arr.slice(max_s, max_e);
}

 

posted on 2022-04-21 09:32  小名香菜~  阅读(76)  评论(0编辑  收藏  举报