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); }