Skip to content

导论

笔记散乱,尚未整理

一个栗子

最大连续子数组

题设

给定一个数组 A[0,...,n-1],求连续子数组,使该子数组和最大。

例如:

数组:1, -2, 3, 10, -4, 7, 2, -5

最大子数组: 3, 10, -4, 7, 2

常用解法

  • 暴力法
  • 分治法
  • 分析法
  • 动态规划法

暴力法

代码

function MaxSubArray(A) {
  let n = A.length;
  let maxSum = A[0];

  let currentSum;
  for(let i = 0; i < n; i++) {
    for(let j = i; j < n; j++) {
      currentSum = 0;
      for(let k = i; k <= j; k++) {
        currentSum += A[k];
      }
      if (currentSum > maxSum) {
        maxSum = currentSum;
      }
    }
  }

  return maxSum;
}

复杂度

  • 时间复杂度 O(n^3)

分治法

算法思想

  • 将数组从中分开,那么最法子数组要么完全在左半边,要么完全在右半边,要么跨立在分界点上。
  • 完全在左/右半边,可以递归解决;
  • 跨立在分界点上: 左数组的最大后缀 + 右数组的最大前缀。

代码

function MaxSub(A, from, to) {
  if (from === to) {
    return A[from];
  }

  let middle = Math.floor((from + to) / 2);
  let max1 = MaxSub(A, from, middle); // 左递归
  let max2 = MaxSub(A, middle + 1, to); // 右递归

  let left = A[middle], now = A[middle]
  for(let i = middle - 1; i >= from; i--) {
    now += A[i];
    left = left >= now ? left : now;
  }

  let right = A[middle + 1];
  now = A[middle + 1];
  for(let i = middle + 2; i <= to; i++) {
    now += A[i];
    right = right >= now ? right : now;
  }
  let max3 = left + right; // 跨立左右

  return max(max1, max2, max3);
}

function max() {
  const args = Array.prototype.slice.call(arguments);

  let _max = args[0];
  args.forEach(i => _max = _max >= i ? _max : i);

  return _max; 
}

复杂度

  • 时间递推关系: T(n) = 2 * T(n - 1) + c * n, c 为常量
  • 所以,时间复杂度: T(n) = O(nlogn)