算法-输出所有和为S的连续正数序列

355 阅读1分钟

题目

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

输入

9

返回值

[[2,3,4],[4,5]]

实现方案

1.三层循环实现

子数组的开始项和结束项分别循环一次,找到所有可能的组合,再用第三层循环累加计算和与S作比较。

function createArray(i, j) {
  const arr = [];
  for (let k = i; k <= j; k++) {
    arr.push(k);
  }
  return arr;
}


function getContinuousSubstring(count) {
  const result = [];
  const middleValue = Math.floor(count / 2);
  for (let i = 1; i <= middleValue; i++) {
    for (let j = i + 1; j < count; j++) {

      let sum = 0;
      for (let k = i; k <= j; k++) {
        sum += k;
      }
      if (sum === count) {
        //找到了
        result.push(createArray(i, j))
      }

    }
  }

  return result;
}

// 验证
console.log('和为15');
console.log(getContinuousSubstring(15));
console.log('和为9');
console.log(getContinuousSubstring(9));
console.log('和为200');
console.log(getContinuousSubstring(200));

验证效果

2.两层循环+等差数列求和公式

和上面的实现方案一样,两层循环用来找到所有开始项和结束项的组合情况,这次不一样的地方是计算子串和的时候不用再遍历一次,直接使用等差数列的求和公式就可以啦。

子串和 = ( (开始项 + 结尾项) / 2 ) * (结尾项 - 开始项 + 1)

function createArray(i, j) {
  const arr = [];
  for (let k = i; k <= j; k++) {
    arr.push(k);
  }
  return arr;
}

function getContinuousSubstring(count) {
  const result = [];
  const middleValue = Math.floor(count / 2);
  for (let i = 1; i <= middleValue; i++) {
    for (let j = i + 1; j < count; j++) {

      const sum = ((i + j) / 2) * (j - i + 1)
      if (sum === count) {
        //找到了
        result.push(createArray(i, j))
      }

    }
  }

  return result;
}

// 验证
console.log('和为15');
console.log(getContinuousSubstring(15));
console.log('和为9');
console.log(getContinuousSubstring(9));
console.log('和为200');
console.log(getContinuousSubstring(200));

验证效果:

3.滑动窗口

滑动窗口设计满足如下这几个原则:
1.用left 和 right 标记窗口的开始项和结尾项;两个都只会右移,如果子串和大于S,则右移left,如果子串和小于S则右移right.
2.如果子串和等于S说明找到了其中一个子串,添加到结果数组中后,将left或right右移一位。
3.刚开始left right都在最左边,继续上面的操作,直到left已经不小于中间项才跳出。

function createArray(i, j) {
  const arr = [];
  for (let k = i; k <= j; k++) {
    arr.push(k);
  }
  return arr;
}

function getContinuousSubstring(count) {
  let left = 1;
  let right = 1;

  const result = [];

  while (left <= Math.floor((count / 2))) {
    const sum = ((left + right) / 2) * (right - left + 1);
    if (sum > count) {
      left++;
    } else if (sum < count) {
      right++;
    } else {
      result.push(createArray(left, right));
      left++;
    }
  }

  return result;
}

// 验证
console.log('和为15');
console.log(getContinuousSubstring(15));
console.log('和为9');
console.log(getContinuousSubstring(9));
console.log('和为200');
console.log(getContinuousSubstring(200));

验证效果 这应该是最好方案,找到窗口的时间复杂度为: O(n)

总结

本文总结了输出所有和为S的连续正数序列的三种实现方案:三层循环,两层循环+等差求和,滑动窗口等。希望能对你有所帮助。