LeetCode每日一题之排列硬币

前言

大家好,今天是LeetCode每日一题的第十天,,给大家分享的是排列硬币,难度系数两颗星!废话不多说,先上题目!

1.1 题目要求

题目类型排列硬币

题目内容

  • 总共有n枚硬币,将它们摆成一个阶梯形状,第k行就必须正好有k枚硬币。

  • 给定一个数字n,找出可形成完整阶梯的总行数。

注意事项n是一个非负整数,并且在32位有符号整型的范围内。

1.2 解题方法

1.2.1 使用迭代法处理
1.解题思路

假设已有n枚硬币,按照题目要求把它们摆成一个阶梯形状,同时要求第k行就必须正好有k枚硬币,那么应该怎样排列呢?

假设硬币总数为10,组成阶梯状情况如下:

行数硬币排列剩余数量大小比较是否结束
109 (10-1)9>2
20 07 (10-1-2)7>3
30 0 04 (10-1-2-3)4=4
40 0 0 03 (10-1-2-3-4)0<5

注意:

  1. 硬币排列中的0表示硬币;
  2. 大小比较是指判断硬币的剩余数量与下一行的硬币个数的大小关系;
  3. 是否结束是指剩余数量是否小于等于下一行个数, 若剩余数小于下一行个数, 则排列结束,并返回当前行数
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讲)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂奔の蜗牛rz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值