LeetCode 441 - 排列硬币


文章目录

摘要

"排列硬币"这道题看起来像小学数学题,但它其实是一道非常适合用二分查找解决的"等差数列"问题。

题目说你有 n 枚硬币,要摆成阶梯形,第 k 行用 k 枚硬币,问你最多能摆满多少行。

如果你硬算每一行累加,当然可以,但 n 最大能到 (2^{31}-1),直接累加可能慢一些。

更优雅的方式是用数学公式 + 二分查找,直接定位可用的最大 k。

描述

你总共有 n **枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i **行必须正好有 i **枚硬币。阶梯的最后一行 可能 是不完整的。

给你一个数字 n **,计算并返回可形成 完整阶梯行 的总行数。

示例 1:

txt 复制代码
输入: n = 5
输出: 2
解释: 因为第三行不完整,所以返回 2 。

示例 2:

txt 复制代码
输入: n = 8
输出: 3
解释: 因为第四行不完整,所以返回 3 。

提示:

  • 1 <= n <= 231 - 1

题目给你 n 枚硬币,它们要排列成下面这样的阶梯:

txt 复制代码
1
2
3
4
...

第 1 行要 1 个,第 2 行要 2 个,第 3 行要 3 个......

第 k 行要 k 个。

我们要找到最大的 k,使得:

txt 复制代码
1 + 2 + 3 + ... + k <= n

也就是:

txt 复制代码
k*(k+1)/2 <= n

我们只需要求出这个 k 即可。

示例:

txt 复制代码
输入: n = 5
排列:1 + 2 + 3 = 6 > 5
最多只能摆 1 + 2 = 3
输出 2
txt 复制代码
输入: n = 8
排列到第三行:1+2+3 = 6
第四行需要 4,总共要 10 > 8
输出 3

题解答案

我们可以用两种方式来做:

  1. 数学 + 二分查找(更推荐)
  2. 线性累加(简单但慢)

最优解肯定是二分查找,因为上面的不等式曲线是递增的。

可运行 Swift 代码(包含二分 + 注释)

这段代码可以直接在 Playground 或 Swift 工程中运行:

swift 复制代码
import Foundation

class Solution {
    func arrangeCoins(_ n: Int) -> Int {
        var left = 1
        var right = n
        
        while left <= right {
            let mid = left + (right - left) / 2
            // k 行所需硬币数量
            let need = mid * (mid + 1) / 2
            
            if need == n {
                return mid
            } else if need < n {
                left = mid + 1
            } else {
                right = mid - 1
            }
        }
        
        return right
    }
}

// Demo 测试
let solution = Solution()

let tests = [5, 8, 1, 3, 10, 100, 2147483647]

for n in tests {
    let result = solution.arrangeCoins(n)
    print("n=\(n) → 完整行数 = \(result)")
}

题解代码分析

来拆解一下为什么这段代码高效。

1. 阶梯的总硬币数公式

第 k 行要 k 个硬币,总数是:

txt 复制代码
1 + 2 + ... + k = k*(k+1)/2

题目要找最大 k,使得:

txt 复制代码
k*(k+1)/2 <= n

这是一个递增函数,可以二分。

2. 二分查找区间

我们设定:

txt 复制代码
left = 1
right = n

不需要从 0 到 n-1,因为 k 至多就是 n 行(虽然不可能到达这么大,但区间这样设更简单)。

每次尝试 mid:

txt 复制代码
need = mid*(mid+1)/2

然后判断:

  • 如果 need == n → 刚刚好,直接返回 mid;
  • 如果 need < n → 说明还能加行,left = mid+1;
  • 如果 need > n → 说明 mid 太大,right = mid-1;

最后,当 left > right 时,区间收缩结束,最大合法的 k 就是 right

3. 为什么返回 right?

因为最后一次合法的行数就在 right 位置:

  • left 会指向第一个不合法的 k;
  • right 就是最大的合法值。

这种方式是标准二分套路。

示例测试及结果

上面 Demo 运行后输出类似:

txt 复制代码
n=5 → 完整行数 = 2
n=8 → 完整行数 = 3
n=1 → 完整行数 = 1
n=3 → 完整行数 = 2
n=10 → 完整行数 = 4
n=100 → 完整行数 = 13
n=2147483647 → 完整行数 = 65535

对于 n 超级大(2^31−1),算法依然能在毫秒级跑完,这就是二分的优势。

时间复杂度

二分查找的区间是 [1, n],迭代次数是:

txt 复制代码
O(log n)

因为每次都减半搜索空间。

空间复杂度

只使用常数级变量:

txt 复制代码
O(1)

没有额外数组,没有递归。

总结

这道题是非常典型的"等差数列 + 二分查找"应用题。

要点总结如下:

  • 本质是求解 k*(k+1)/2 <= n 的最大 k;
  • 使用二分查找可以在 O(log n) 内解决;
  • 线性求解虽然能过,但不够优雅;
  • Swift 中用整型计算时不会溢出,因为 mid*(mid+1) 在范围内可安全计算;
  • 可作为练习"单调函数 + 二分查找"的经典案例之一。
相关推荐
漂流瓶jz几秒前
UVA-1152 和为0的4个值 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·二分查找·题解·aoapc·算法竞赛入门经典·uva
leoufung9 分钟前
LeetCode 76:Minimum Window Substring 题解与滑动窗口思维详解
算法·leetcode·职场和发展
小O的算法实验室22 分钟前
2026年IEEE TETCI,山区环境下基于双种群进化的协同无人机巡逻任务协同优化,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
生成论实验室42 分钟前
《事件关系阴阳博弈动力学:识势应势之道》第二篇:阴阳博弈——认知的动力学基础
数据结构·人工智能·科技·神经网络·算法
风筝在晴天搁浅1 小时前
字节高频题 小于n的最大数
算法
LabVIEW开发1 小时前
LabVIEW水力机组空蚀在线监测
算法·labview·labview知识·labview功能·labview程序
AI科技星1 小时前
科幻艺术书本封面:《全域数学》第一部·数术本源 第三卷 代数原本(P95-141)完整五级目录【乖乖数学】
算法·机器学习·数学建模·数据挖掘·量子计算
风筝在晴天搁浅1 小时前
LeetCode 92.反转链表Ⅱ
算法·leetcode·链表
weisian1511 小时前
基础篇--概念原理-2-参数是什么?——从原理到实战,一篇讲透
面试·职场和发展·模型参数·7b和70b·参数=规则,不是原始数据
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【贪心与二分判定】:数列分段 Section II
c++·算法·贪心·csp·信奥赛·二分判定·数列分段 section ii