Ugly Number

来源:互联网 发布:淘宝有多少的店铺 编辑:程序博客网 时间:2024/05/21 22:53

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