题目
You have a total of n coins that you want to form in a staircase shape, where everyk-th row must have exactlyk coins.
Given n, find the total number of full staircase rows that can be formed.
n is a non-negative integer and fits within the range of a 32-bit signed integer.
Example 1:
n = 5
The coins can form the following rows:
¤
¤ ¤
¤ ¤
Because the 3rd row is incomplete, we return 2.
Example 2:
n = 8
The coins can form the following rows:
¤
¤ ¤
¤ ¤ ¤
¤ ¤
Because the 4th row is incomplete, we return 3.
题意
你一共有n个硬币,你需要将其摆成阶梯的形状,在这个阶梯中 第 k行 一定要有 k个硬币。
给定一个n,找出可以形成的完整阶梯的总数。
n是一个非负整数,在带符号的32位整数范围内。
注意:
(1)关于数值类型的范围
- 【笔试题】大部分笔试题主要用到的数据类型为整数类型,这时候double这些具有高位数的便可以 当作辅助工具。
- 【正常情况】这到题目看似比较简单,我们能够轻易解出 n 为正常值得情况。
- 【数值类型的范围】32位有符号整数 范围为: -2^31 ~ 2^31-1( 2 147 483 647)
- 【非正常情况 + 难点】你需要考虑当 n 非常接近于32位正整数的最大值得情况,此时你对于n的运算(如:与整数的除法,平方)可能导致数值溢出。举个简单的例子:假如n =1 804 289 383(< 2 147 483 647), 这意味着 n *2 就会超过32位整数的最大值,进而导致错误(在java中输出结果为:-686388530)。 ----> 所以,在我们的程序中一定得小心对于整数的运算。
- 【需要注意的运算】 加法 , 乘法,平方等,这些计算都会使数值增加。
(2)关于二分查找
下面简单说说二分查找的一些特征:
- 【作用】二分查找基本上每次都会将范围降低为原来的一半。
- 【适用的场景】二分查找 需要建立在 待查找数值具有次序的基础上。
分析及解答
借鉴网友的算法,具体的连接可以见:(网友的算法及解释)
接下来,我会从两个角度来说明这个算法需要注意的地方:
- 控制数值的范围
- 二分查找
(1)控制数值的范围
- 【技巧:整数范围有限,所以转化为 double类型,范围变广】mid * mid如果计算结果被认为是整数的情况的话,那么在运行的时候一定会溢出,所以作者巧妙的在前面加了0.5,也就是告诉了编译器:“经过计算后,我变成了double类型的数据了”。 ----> 而在java中 double 类型的数值,是64位的,远远大于32位的int 。
0.5 * mid * mid
(2)二分查找
- 【为何用二分查找?】在该题目中,需要计算一定范围的数的和,然后与 n 进行比较。仔细观察可以发现,起点是固定的,主要的问题就在于确定终点,再加上具有次序性,于是便可以使用二分查找。
- 【核心公式】假如说最大行为 m ,那么 m 满足 :m * (m +1)/ 2 <= n.
代码:
public class Solution {
public int arrangeCoins(int n) {
int start = 0;
int end = n;
int mid = 0;
while (start <= end){
mid = (start + end) >>> 1;
if ((0.5 * mid * mid + 0.5 * mid ) <= n){
start = mid + 1;
}else{
end = mid - 1;
}
}
return start - 1;
}
}
我的解法(低效算法)
* 为何低效?
public class Solution {
public int arrangeCoins(int n) {
if(n == 0) return 0;
if(n == 1) return 1;
int i =0;
i = (int)(Math.sqrt(n*2.0)); // java封装的开方本身就会消耗大量的时间。
for(; 0.5*i*(i+1) > n ;i--){
}
return i;
}
}