剑指-丑数

题目:
我们把只包含因子2,3,5的数称为丑数(ugly number). 求从小到大的顺序第1500个丑数. 例如6,8都是丑数,但是14不是,因为它包含因子7.习惯上我们把1当做一个丑数.

思路 1:
逐个判断每个整数是不是丑数的方法,直观但不够高效. 其最大的问题是,每个数字都需要计算,即时一个数字是不是丑数,我们还是需要对它做求余和除法操作.该算法效率非常低,16秒左右.

思路2:
创建数组保存已经找到的丑数,用空间换时间,时间花费0.001秒左右.

根据丑数的定义,丑数应该是另一个丑数乘以2,3,5的结果.因此我们可以创建一个数组,里面的数字是排序好的丑数,每一个丑数都是前面的丑数乘以2,3,5得到的.这种思路的关键在于怎样确保数组里面的丑数是排序好的.

假设数组中已经有若干个丑数排序好后存放在数组中,并且把已有最大的丑数记作M,接下来分析如何生成下一个丑数. 该丑数肯定是前面的某一个丑数乘以2,3,5,的结果,所以我们首先考虑把已有的每个丑数乘以2.在乘以2的时候,能够得到若干小于或等于M的结果. 由于是按照顺序生成的,小于或等于M肯定已经在数组中了,我们不需要再次考虑.还会得到若干大于M的结果,但是我们只需要第一个大于M的结果. 我们把第一个乘以2以后大于M的结果记为M2.同样,我们把已有的丑数乘以3和5,能得到第一个大于M的结果M3和M5.那么下一个丑数应该是M2,M3,M5这三个数的最小值.

前面分析的时候,提到把已有的每个丑数分别乘以2,3,5.事实上这不是必须的,因为已有的丑数是按照顺序存放在数组中的. 对于乘以2而言,肯定存在某一个丑数T2,排在它之前的每一个丑数乘以2得到的记过都小于已有的最大丑数,在它之后的每一个丑数乘以2的到的结果都会太大. 我们只需要记下这个位置,同时每次生成新的丑数的时候,去更新这个T2. 对于乘以3和5而言,也存在相同的T3和T5.

public static void main(String[] args) {
    // 16s
//        long start = System.currentTimeMillis();
//        System.out.println(getUglyNumber(1500));
//        System.out.println((System.currentTimeMillis() - start) / 1000f);

    long start = System.currentTimeMillis();
    System.out.println(getUglyNumber2(1500));
    System.out.println((System.currentTimeMillis() - start) / 1000f);

}

static int getUglyNumber2(int index) {
    if (index <= 0) {
        return 0;
    }

    int[] uglyNumbers = new int[index];

    uglyNumbers[0] = 1;

    int nextUglyIndex = 1;

    // T2 T3 T5
    int multiply2Index = 0;
    int multiply3Index = 0;
    int multiply5Index = 0;


    while (nextUglyIndex < index) {

        // M2,M3,M5中取最大的.
        int min = min(uglyNumbers[multiply2Index] * 2, uglyNumbers[multiply3Index] * 3, uglyNumbers[multiply5Index] * 5);

        uglyNumbers[nextUglyIndex] = min;

        // 更新T2,T3,T5
        while (uglyNumbers[multiply2Index] * 2 <= uglyNumbers[nextUglyIndex]) {
            multiply2Index++;
        }
        while (uglyNumbers[multiply3Index] * 3 <= uglyNumbers[nextUglyIndex]) {
            multiply3Index++;
        }
        while (uglyNumbers[multiply5Index] * 5 <= uglyNumbers[nextUglyIndex]) {
            multiply5Index++;
        }

        nextUglyIndex++;
    }

    return uglyNumbers[nextUglyIndex - 1];

}

static int min(int n1, int n2, int n3) {
    int min = (n1 < n2) ? n1 : n2;
    return min < n3 ? min : n3;
}


static boolean isUgly(int number) {
    while (number % 2 == 0) {
        number /= 2;
    }

    while (number % 3 == 0) {
        number /= 3;
    }
    while (number % 5 == 0) {
        number /= 5;
    }

    return number == 1;
}

static int getUglyNumber(int index) {
    if (index <= 0) {
        return 0;
    }

    int number = 0;
    int uglyFound = 0;
    while (uglyFound < index) {
        ++number;
        if (isUgly(number)) {
            ++uglyFound;
        }
    }
    return number;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值