Java 实现丑数

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

代码

解法一

所谓一个数m是另一个数n的因子,是指n能被m整除,也就是说n%m==0.

    public static int[] findUglyNumbers(int n) {
        if (n <= 0) {
            return null;
        }
        int[] result = new int[n];
        result[0] = 1;
        int index = 1;
        int calNumber = 1;
        while (index < n) {
            calNumber++;
            // 是不是丑数都要计算一次
            if (isUglyNumber(calNumber)) {
                result[index] = calNumber;
                index++;
            }
        }
        return result;
    }

    /**
     * 判断一个数是否是丑数,即只包含因子2,3,5的数,即
     * 如果可以被2整除,就连续除以2,直到不能被2整除为止
     * 如果可以被3整除,就连续除以3,直到不能被3整除为止
     * 如果可以被5整除,就连续除以5,直到不能被5整除为止
     * 如果最后的结果是1,说明是丑数,否则不是
     * 例如,30,可以被2整除,然后除以2得到15
     *      15,可以被3整除,然后除以3得到5
     *      5,可以被5整除,然后除以5得到1
     * @param n
     * @return
     */
    private static boolean isUglyNumber(int n) {
        while (n % 2 == 0) {
            n /= 2;
        }
        while (n % 3 == 0) {
            n /= 3;
        }
        while (n % 5 == 0) {
            n /= 5;
        }

        return n == 1;
    }

解法二

只计算丑数,而不计算非丑数。丑数=丑数*(2/3/5),所以创建数组保存有序的丑数。关键在于如何在计算丑数的过程中保持数组有序。当前的丑数必然是之前某一个丑数*因子的结果,但是不需要每个数都要乘一遍2、3、5。要获得的丑数必然是大于现在已有的,在计算得出丑数中选择一个最小的放入数组中,来保持数组的有序,因为新放入的丑数是根据之前的丑数计算得到的,所以必然是有序的。为了每次新得到的三个丑数都是比已有丑数大,且最小,所以要记录各个因子下次计算要使用的已有丑数在什么位置,否则就会出现跳数,比如已有{1,2,3,4},我们知道下一个丑数应该是5,但是如果因子5没有选择第一个丑数1来相乘,就会漏掉5这个丑数。

    public static int[] findUglyNumbers2(int n) {
        if (n <= 0) {
            return null;
        }
        int[] result = new int[n];
        // 1作为第一个丑数
        result[0] = 1;
        // 初始2,3,5因子对应的乘数为第一个丑数,即1
        int divisor2 = 0;
        int divisor3 = 0;
        int divisor5 = 0;
        // result={1}
        // result[divisor2=0]=1, result[divisor3=0]=1, result[divisor5=0]=1,  multiply2=2, multiply3=3, multiply5=5
        // min = multiply2,所以 此时divisor2=1, divisor3=0, divisor5=0
        // result={1, 2}
        // result[divisor2=1]=2, result[divisor3=0]=1, result[divisor5=0]=1,  multiply2=4, multiply3=3, multiply5=5
        // min = multiply3,所以 此时divisor2=1, divisor3=1, divisor5=0
        // result={1, 2, 3}
        // result[divisor2=1]=2, result[divisor3=1]=2, result[divisor5=0]=1,  multiply2=4, multiply3=6, multiply5=5
        // min = multiply2,所以 此时divisor2=3, divisor3=1, divisor5=0
        // result={1, 2, 3, 4}
        // result[divisor2=3]=4, result[divisor3=1]=2, result[divisor5=0]=1,  multiply2=8, multiply3=6, multiply5=5
        // min = multiply5,所以 此时divisor2=3, divisor3=2, divisor5=1
        // result={1, 2, 3, 4, 5}
        // result[divisor2=3]=4, result[divisor3=2]=3, result[divisor5=1]=2,  multiply2=8, multiply3=6, multiply5=10
        // min = multiply3,所以 此时divisor2=3, divisor3=3, divisor5=1
        // result={1, 2, 3, 4, 5, 6}
        for (int i = 1; i < n ; i++) {
            // 根据因子对应的乘数下表计算得出下一个丑数
            int multiply2 = result[divisor2] * 2;
            int multiply3 = result[divisor3] * 3;
            int multiply5 = result[divisor5] * 5;
            // 找出三个因子乘以上一个对应丑数后最小的那个丑数,以此来保证丑数有序
            int min = Math.min(multiply2, Math.min(multiply3, multiply5));
            // 将最小的丑数放入结果集中,用于下一次计算
            result[i] = min;
            // 找出对应此次计算最小丑数的因子,并移动指针指向下一次计算薪丑数对应的老丑数下标
            if (multiply2 == result[i]) {
                divisor2++;
            }
            if (multiply3 == result[i]) {
                divisor3++;
            }
            if (multiply5 == result[i]) {
                divisor5++;
            }
        }
        return result;
    }

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值