Ugly Number

Ugly Number
问题描述:只含有素因子2,3,5的数,称为Ugly Number,并且认为1是Ugly Number。eg. 1,2,3,4,5,6,8,9,10,12,…

问题1

给定一个数num,判断其是否为Ugly Number,是则返回true,否则返回false。

分析:判断这个数是否可以被2或者3或者5整除,并且循环判断,直到结果为1,则则这个数是Ugly Number,返回true,否则返回false。

public boolean isUglyNumber(int m) {
        if(m <= 0) {
            return false;
        }
        while(m != 1) {
            if(m % 2 == 0) {
                m /= 2;
            } else if(m % 3 == 0) {
                m /= 3;
            } else if(m % 5 == 0) {
                m /= 5;
            } else {
                return false;
            }
        }
        return true;
    }

显然,这个算法的时间复杂度是O(logN)。

问题2

给定一个数n,返回Ugly Number列表的第n个数。Ugly Number列表为1,2,3,4,5,6,8,9,10,12,15,....。eg.

n为1时,返回1;
n为7时,返回8;
n为10时,返回12;

题目链接:http://www.lintcode.com/zh-cn/problem/ugly-number-ii/
分析:
方法1:可以使用一个循环,从1开始,判断每一个数是不是Ugly Number,直到得到第n个数。

public int nthUglyNumber(int n) {
        int sum = 0;
        int i = 1;
        while(sum < n) {
            if(isUglyNumber(i)) {
                sum++;
            }
            i++;
        }
        return i - 1;
    }

显然,这个方法的时间复杂度是O(N*logN)。在lintCode上提交的时候,会发现超过时间限制,所以还需要更好的方法。
方法2:可以换一个思路,我们可以用一个数组,来手动生成一个Ugly Number的列表。就如同Fibonacci数列一样,后边的数字,总是可以由前边的数字得到。
假设A是一个长度为n的int数组,起始的时候A[0]=1,其它元素均为default值0。后边的元素均可以通过A[0]和因子2,3,5相乘来得到。
第一步: A[0]=1;
第二步: 取min(2*A[0], 3*A[0], 5*A[0])即2*A[0]=2作为A[1]。此时,A[0]=1,A[1]=2,则后边的元素可以通过A[0]和A[1]和因子2,3,5相乘来得到;
第三步: 由于在第二步中2*A[0]已经作为了A[1],所以,2的因子的数组下标应该加1,即由A[0]变为A[1],取min(2*A[1], 3*A[0], 5*A[0])即3*A[0]=3作为A[2]。此时,A[0]=1,A[1]=2,A[2]=3。后边的元素可以通过A[0],A[1],A[2]和因子2,3,5相乘来得到;
第四步: 以此类推,A[3]=min(2*A[1], 3*A[1], 5*A[0])=2*A[1]=4;
第五步: A[4]=min(2*A[2], 3*A[1], 5*A[0])=5*A[0]=5;
第六步: A[5]=min(2*A[2], 3*A[1], 5*A[1])=6,注意,此时2*A[2]和3*A[1]的值均为最小值6,所以在下一步中,2和3的因子的数组下标都应该加1,即2*A[3]和3*A[2];
第七步: A[6]=min(2*A[3], 3*A[2], 5*A[1])=2*A[3]=8;

以此类推,得到A[n-1]即为题目所求。

public int nthUglyNumber(int n) {
        if(n <= 0) {
            return 0;
        }
        int [] nums = new int[n];
        nums[0] = 1;
        int index2 = 0, index3 = 0, index5 = 0;

        for(int i = 1; i < n; i++) {
            nums[i] = Math.min(2 * nums[index2], Math.min(3 * nums[index3], 5 * nums[index5]));
            if (nums[i] == 2 * nums[index2]) {
                index2++;
            }
            if (nums[i] == 3 * nums[index3]) {
                index3++;
            }
            if(nums[i] == 5 * nums[index5]) {
                index5++;
            }           
        }
        return nums[n - 1];
    }

显然,方法2的时间复杂度为O(N)。

问题3

Super Ugly Number,给定一个素数数组primes和一个正整数n,求使用这个数组构建的Ugly Number的第n个数。这个问题是问题2的一般化,在问题2中,相当与primes是{2,3,5}。

题目链接:http://www.lintcode.com/zh-cn/problem/super-ugly-number/
分析: 与问题2的第二种思路一样,这里使用一个数组来保存下标,相当于上面的index2,index3,index5。

/**
     * @param n a positive integer
     * @param primes the given prime list
     * @return the nth super ugly number
     */
    public int nthSuperUglyNumber(int n, int[] primes) {
        if(n <= 0) {
            return 0;
        }
        int []nums = new int[n];
        int []index = new int[primes.length];
        nums[0] = 1;
        for(int i = 1; i < n; i++) {
            //find min number as nums[i]
            int min = primes[0] * nums[index[0]];
            for(int j = 1; j < primes.length; j++) {
                if(primes[j] * nums[index[j]] < min) {
                    min = primes[j] * nums[index[j]];
                }
            }
            nums[i] = min;
            //update index
            for(int j = 0; j < primes.length; j++) {
                if(min == primes[j] * nums[index[j]]) {
                    index[j]++;
                }
            }
        }
        return nums[n - 1];
    }

假设primes的length为M,显然,这个方法的时间复杂度是O(N*M)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值