LeetCode探索(82):829-连续整数求和

221 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

题目

给定一个正整数 n,返回 连续正整数满足所有数字之和为 n 的组数

示例 1:

输入: n = 5
输出: 2
解释: 5 = 2 + 3,共有两组连续整数([5],[2,3])求和后为 5

示例 2:

输入: n = 9
输出: 3
解释: 9 = 4 + 5 = 2 + 3 + 4

示例 3:

输入: n = 15
输出: 4
解释: 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5

提示:

  • 1 <= n <= 10^9

思考

本题难度困难。

首先是读懂题意。给定一个正整数 n,我们返回连续正整数满足所有数字之和为 n 的组数。 需要注意的是这里是连续正整数,且正整数 n 本身就是符合要求的一个答案。

我们试着分析题目。对于连续正整数,符合等差数列的定义,假设首项为 a,项数为 k,和为 n,那么

(a + a + k - 1) * k / 2 = n => (2a + k - 1) * k = 2n (1)

2a = 2n / k - k + 1 >= 2 ==> 2n / k >= k + 1 ==> 2n / k > k (2)

由(1)和(2)可知,k 是 2n 的约数,并且为较小的约数。

因此我们可以在[1,2n)[1, \sqrt{2n})范围内枚举 k,如果 k 为 2n 约数,并且结合 (1) ,说明找到了一组合法的 (a, k),对答案进行累加。

解答

方法一:

/**
 * @param {number} n
 * @return {number}
 */
var consecutiveNumbersSum = function(n) {
  let ans = 0
  n *= 2 // n为2n
  // 在 [1, (2n)^1/2) 范围内枚举 k
  for (let k = 1; k * k < n; k++) {
    // k 是 2n 的约数,并且为较小的约数
    if (n % k !== 0) {
      continue
    }
    // 判断条件
    if ((n / k - k + 1) % 2 === 0) {
      ans++
    }
  }
  return ans
}

// 执行用时:52 ms, 在所有 JavaScript 提交中击败了98.00%的用户
// 内存消耗:40.9 MB, 在所有 JavaScript 提交中击败了82.00%的用户
// 通过测试用例:170 / 170

复杂度分析:

  • 时间复杂度:O(2n)O(\sqrt{2n})。
  • 空间复杂度:O(1)。

参考