【刷穿leetcode】441. 排列硬币|刷题打卡

250 阅读3分钟

一、题目描述:

leetcode441题:排列硬币

你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。
给定一个数字 n,找出可形成完整阶梯行的总行数。
n 是一个非负整数,并且在32位有符号整型的范围内。

示例 1:

n = 5

硬币可排列成以下几行:
¤
¤ ¤
¤ ¤

因为第三行不完整,所以返回2.

示例 2:

n = 8

硬币可排列成以下几行:
¤
¤ ¤
¤ ¤ ¤
¤ ¤

因为第四行不完整,所以返回3.

二、思路分析:

解法1:暴力循环

暴力循环是直观的解法,对n枚硬币进行循环遍历,解题思路为:

  1. 当n为1或者0时,返回n;
  2. 遍历累加记录sum,循环变量i为当前阶梯行数,当sum>n,所需要硬币数超过已有硬币,返回i-1。

复杂度分析:

  • 时间复杂度:O(n),其中n为硬币枚数,最坏情况需要进行n次循环查找,时间复杂度为O(n)。
  • 空间复杂度:O(1),未产生额外空间开销。

解法2:二分查找

由题意可知,形成0行阶梯一共需要0个硬币,形成1行阶梯一共需要1个硬币,形成2行阶梯一共需要3个硬币,形成x行阶梯一共需要 1+2+...+x = (1+x)x/2个硬币。

换句话说,形成0、1、2、3……n 行阶梯需要的硬币数是 [0, 1, 3, 6, ...., (1+n)n/2],可以看出是一个递增序列。现在题目问“有n个硬币时,能形成的完整阶梯行数”,抽象后就是“给你一个递增数组 nums,要求你返回 target 的位置,如果 target 不存在就返回距离 target 最近的左侧元素的位置”。

复杂度分析:

  • 时间复杂度:O(log2n),其中n为硬币枚数,采用二分查找target的位置,最坏情况用O(log2n)完成搜索。
  • 空间复杂度:O(1),未产生额外空间开销。

三、AC 代码:

解法1:暴力循环

var arrangeCoins = function(n) {
  if(n <= 1){return n}
  let sum = 0;
  //遍历累加,如果超过n了,返回第a-1次
  for(let i = 1;i<=n;i++){
    sum += i;
    if(sum>n) {
      return i-1;
    }
  }
};

解法2:二分查找

var arrangeCoins = function(n) {
  if (n == 0) {
    return 0;
  }
  var left = 0;
  // 只有 n 个硬币的情况下,最大肯定不会超过 n 行,所以这里把搜索的右侧界限定为 n
  var right = n;
  while (left <= right) {
    var mid = left + ((right - left) >> 1);
      // 形成 mid 行的阶梯一共需要 costToFinishMid 个硬币,这里是数学公式
    var costToFinishMid = (1 + mid) * mid / 2;
    if (costToFinishMid == n) {
      return mid;
    } else if (costToFinishMid < n) {
      left = mid + 1;
    } else if (costToFinishMid > n) {
      right = mid - 1;
    }
  }
  // 按照上述这种写法,right 在这里指向距离 target 最近的左侧元素的位置
  return right;
};

四、总结:

当硬币数n较小时,暴力循环解法既简单,执行效率也不差;当n是较大的正整数,我们就需要使用二分查找来解决,减小时间复杂度。排列硬币,此问题有着单调递增的特点,适合采用二分查找来解决。

本文正在参与「掘金 3 月闯关活动」,点击查看活动详情