前言:
大家好,今天是LeetCode每日一题的第十天,,给大家分享的是排列硬币,难度系数两颗星!废话不多说,先上题目!
1.1 题目要求
题目类型:排列硬币
题目内容:
-
总共有n枚硬币,将它们摆成一个阶梯形状,第k行就必须正好有k枚硬币。
-
给定一个数字n,找出可形成完整阶梯的总行数。
注意事项:n是一个非负整数,并且在32位有符号整型的范围内。
1.2 解题方法
1.2.1 使用迭代法处理
1.解题思路
假设已有n枚硬币,按照题目要求把它们摆成一个阶梯形状,同时要求第k行就必须正好有k枚硬币,那么应该怎样排列呢?
假设硬币总数为10,组成阶梯状情况如下:
行数 | 硬币排列 | 剩余数量 | 大小比较 | 是否结束 |
---|---|---|---|---|
1 | 0 | 9 (10-1) | 9>2 | 否 |
2 | 0 0 | 7 (10-1-2) | 7>3 | 否 |
3 | 0 0 0 | 4 (10-1-2-3) | 4=4 | 否 |
4 | 0 0 0 0 | 3 (10-1-2-3-4) | 0<5 | 是 |
注意:
- 硬币排列中的0表示硬币;
- 大小比较是指判断硬币的剩余数量与下一行的硬币个数的大小关系;
- 是否结束是指剩余数量是否小于等于下一行个数, 若剩余数小于下一行个数, 则排列结束,并返回当前行数
2.代码实现
- 测试代码
package com.kuang.leetcode9;
/**
* @ClassName ArrangeCoin
* @Description 排列硬币
* @Author 狂奔の蜗牛rz
* @Date 2021/8/30
*/
public class ArrangeCoin {
//主方法测试
public static void main(String[] args) {
//测试存在10个硬币时,形成完整阶梯形状返回的总行数
System.out.println(arrangeCoins(10));
}
/**
* 使用迭代法解决硬币排列问题
* @param n 硬币总数(在32位有符号整型范围内的非负整数)
* @return 返回值为(形成完整阶梯形状)总行数
*/
public static int arrangeCoins(int n) {
//编写for循环体
/**
* 定义变量i, 表示行数
* 指针i的初值为多少?
* 要从第一行开始, 所以其初值为1
* i的取值范围为多少?
* 硬币数量i不能超过总数n, 即 i <= n
*/
for (int i = 1; i <= n; i++) {
/**
* 由于题干中要求第i行的元素也为i个,
* 因此硬币剩余数量为总数n减去当前行的硬币个数i
*/
n = n - i;
//判断n(剩余硬币数量)是否小于等于i(当前行的硬币个数)
if(n <= i) {
//若n(剩余数)小于i(当前行个数), 将当前行的个数i进行返回
return i;
}
}
//若当前硬币数不能组成阶梯状, 则返回的排列行数为0
return 0;
}
}
- 测试结果
结果:测试结果与预期相同!
1.2.2 使用二分法查找处理
1.解题思路
-
假设最多可以排列n行,x是第n行的硬币个数,那么每行排列的硬币个数分别为 1 2 3 4 … x;
-
同时n为每行硬币相加之和 (即满足n=1+2+3+4+…+x),也就是说从1到n行,会存在一个x值,使得等式1+2+3+ … +x=n成立;
因此我们可以通过使用二分法从1到n行定位到那个x值
2.代码实现
- 测试代码
package com.kuang.leetcode9;
/**
* @ClassName ArrangeCoin
* @Description 排列硬币
* @Author 狂奔の蜗牛rz
* @Date 2021/8/30
*/
public class ArrangeCoin {
//主方法测试
public static void main(String[] args) {
//测试存在10个硬币时,形成完整阶梯形状返回的总行数
System.out.println("使用二分法排列硬币:"+arrangeCoins2(10));
}
/**
* 使用二分查找法处理硬币排列
* @param n 硬币总数(在32位有符号整型范围内的非负整数)
* @return 返回值为(形成完整阶梯形状)总行数
*/
public static int arrangeCoins2(int n) {
//定义low和high变量, 分别表示左右指针(其初值分别为左右边界值)
int low = 0, high = n;
//while循环的执行条件(low左指针要小于等于high右指针)
while (low <= high) {
/**
* mid为使用二分法查找的中值
* ----|-----|-----|---->
* low mid high
* |<-------->| mid ?
* |<-------->|
* high - low
* |<--->|
* (high-low)/2
* |<-->|<--->|
* low + (high-low)/2 = mid
*/
int mid = (high - low)/2 + low;
/**
* sum和为1到x之和, 即sum=1+2+3+4+..+x
* 化简后为 1/2 * (x+1) * x = ((x+1) * x)/2
*/
int sum = ((mid + 1) * mid) /2;
//判断sum(硬币之和)是否等于n(硬币总数)
if (sum == n) {
/**
* 若sum(硬币之和)值等于n(硬币总数), 说明已经排列完毕
* 则直接返回mid值, 此时mid值即为完成梯形排列的最后行数
*/
return mid;
/**
* 若sum(硬币之和)值大于n(硬币总数), 说明取值范围太大,
* 因此将右指针high向左移动一位(即向上移动一行)
*/
} else if (sum > n) {
//右指针high左移一位
high = mid - 1;
/**
* 若sum(硬币之和)值小于n(硬币总数), 说明取值范围太小,
* 因此将左指针low向右移动一位(即向下移动一行)
*/
} else {
//左指针low右移一位
low = mid + 1;
}
}
//最后将high右指针值进行返回(此时high值为满足排列的总行数)
return high;
}
}
- 测试结果
结果:测试结果与预期相同!
1.2.3 使用牛顿迭代法处理
1.解题思路
牛顿迭代公式:(x + n / x) / 2
需要满足的条件:(x + 1)x / 2 = n
我们将它进一步进行转化:
(x + 1) x = 2n => x * x + x = 2n => x * x = 2n - x
即x的平方等于2n减去x,x的平方相当于牛顿迭代公式中的n
我们只需将x的平方代入到上面的牛顿迭代公式中去,直到求出预期结果为止!
2.代码实现
- 测试代码
package com.kuang.leetcode9;
/**
* @ClassName ArrangeCoin
* @Description 排列硬币
* @Author 狂奔の蜗牛rz
* @Date 2021/8/30
*/
public class ArrangeCoin {
//主方法测试
public static void main(String[] args) {
//测试存在10个硬币时,形成完整阶梯形状返回的总行数
System.out.println("使用牛顿迭代法排列硬币:"+arrangeCoins3(10));
}
/**
* 牛顿迭代求平方根
* @param x 排列总行数(未知值)
* @param n 硬币总个数(已知值)
* @return
*/
private static double sqrt(double x, int n) {
/**
* 定义res返回结果值
* 牛顿迭代公式: (x + n/x)/2
* 满足条件代入: n = x * x = 2n - x
* 即res值为(x + (2n -x)/x)/2
*/
double res = (x + (2 * n - x)/x)/2;
//判断res(返回结果)值是否等于x(排列总行数)
if (res == x) {
//若满足条件, 则将x值返回(此时x值为满足条件的排列总行数)
return x;
} else {
//若不满足条件, 则进行递归调用(将res结果值作为下一轮的x值)
return sqrt(res, n);
}
}
}
- 测试结果
结果:测试结果与预期相同!
好了,今天LeetCode每日一题—排列硬币到这里就结束了,欢迎大家学习和讨论,点赞和收藏!
参考视频链接:https://www.bilibili.com/video/BV1Ey4y1x7J3 (国内算法宝典-LeetCode算法50讲)