lintcode :Ugly Numbers 丑数

题目

设计一个算法,找出只含素因子357 的第 k 大的数。

符合条件的数如:3,5,7,9,15......

样例

如果k=4, 返回 9

挑战

要求时间复杂度为O(nlogn)或者O(n)

解题

法一:直接暴力,逐次判断一个数是不是丑数

下面只对其中的奇数判断是否是丑数,加不加奇数都超时。

class Solution {
    /**
     * @param k: The number k.
     * @return: The kth prime number as description.
     */
    public long kthPrimeNumber(int k) {
        // write your code here
        int i = 0;
        int num = 3;
        while( i < k){
            if(isUgly(num)){
                i++;
            }
            num += 2;
        }
        return num - 2;
    }
    public boolean isUgly(long num){
        while(num%3 == 0){
            num = num/3;
        }
        while(num%5 ==0){
            num = num/5;
        }
        while(num%7==0){
            num = num/7;
        }
        return num==1;
    } 
};

法二:直接求丑数

参考链接

最小的连续三个丑数是:3,5,7.以后的每个丑数其因子也只能是3 5 7 那么我们就可以看作:每个丑数可以写成num = 3i* 5j*7k   ,每个丑数也一定是其前面的丑数乘以 3 或5 或7 得到的数,这样我们可以以3 5 7 作为丑数的基数数组,这个丑数数组中的每个数分别乘以 3, 5, 7,得到三个丑数数组,当然其中也一定会有重复的,选取不在原丑数数组中的最小的那个数,加入的丑数数组中,丑数数组的元素就增加一个。

问题是:对三个数字,如何分别找乘以3 5 7 的最小值?

假设先找乘以3 的最小值,由于原始丑数数组是有序的,则第一个不在丑数数组中的那个数字就是最小的,对于 5 7 同理,第一个不在丑数数组中的那个数就是最小的丑数,或者说是第一个大于所有丑数的那个数就是最小的丑数,这样,我们就找到了三个丑数。最小的丑数加入的丑数数组中。

class Solution {
    /**
     * @param k: The number k.
     * @return: The kth prime number as description.
     */
    public long kthPrimeNumber(int k) {
        // write your code here
        long u[] = new long[k+1];
        u[0] = 1;
        int index = 1;
        int i1 = 0,i2 = 0,i3 = 0;
        long MIN = 0;
        while(index <= k){
            MIN = Math.min(u[i1] * 3 ,Math.min(u[i2] * 5,u[i3] * 7));
            u[index++] = MIN;
            i1 = 0;
            i2 = 0;
            i3 = 0;
            while( u[i1] *3 <= MIN){
                i1++;
            }
            while(u[i2] *5 <= MIN){
                i2++;
            }
            while(u[i3] *7 <= MIN){
                i3++;
            }
        }
        return u[k];
    }
};
Java Code

总耗时: 752 ms

但是通过上面程序中,每次都要遍历一遍丑数数组,时间复杂度比较长,但是也竟然AC了,同时里面重读的数也很多。我们可以记录中间的某个丑数的下标 ui来实现,现在其之前的数乘以3都会小于于最大的丑数,其之后的数乘以3都大于最大的丑数。5 7 同理。这样,我们可以在原先找到的满足最小大于丑数数组最大值的那个原始的下标来考虑。

设 3 5 7对应的下标是:i1  i2 i3 这里的下标意思:u[i1]*2是第一个大于最大丑数数组最大值的数。i1 i2 i3 可以理解为不同步的三个指针

当 i1是三个最小丑数中的最小值,则u[i1]*3 加入到丑数数组中,u[i1]*3 就是当前丑数数组的最大值,同时i1+=1 这里进行加1 处理,因为其i1之前的数乘以2的值一定小于u[i1]*3 ,并且这个元素已经在丑数数组u中了,u[i1 + 1] * 3 也就是第一个大于丑数数组最大值的数组。此时对于  i2 i3 不做处理。其他情况类似。

class Solution {
    /**
     * @param k: The number k.
     * @return: The kth prime number as description.
     */
    public long kthPrimeNumber(int k) {
        // write your code here
        long u[] = new long[k+1];
        u[0] = 1;
        int index = 1;
        int i1 = 0,i2 = 0,i3 = 0;
        long MIN = 0;
        while(index <= k){
            MIN = Math.min(u[i1] * 3 ,Math.min(u[i2] * 5,u[i3] * 7));
            u[index++] = MIN;
            if(MIN == u[i1] * 3){
                i1 += 1;
            }
            if(MIN == u[i2] * 5){
                i2 +=1;
            }
            if(MIN == u[i3] * 7){
                i3 +=1;
            }
        }
        return u[k];
    }
};
Java Code

总耗时: 1241 ms

 用ArrayList

class Solution {
    /**
     * @param k: The number k.
     * @return: The kth prime number as description.
     */
    public long kthPrimeNumber(int k) {
        // write your code here
        int i1 = 1;
        int i2 = 1;
        int i3 = 1;
        List<Long> list = new ArrayList<Long>();
        
        list.add((long)1);
        while(list.size() <= k){
            long u1 = list.get(i1-1) * 3;
            long u2 = list.get(i2-1) * 5;
            long u3 = list.get(i3-1) * 7;
            long min = Math.min(u1,Math.min(u2,u3));
            if(min == u1)
                i1++;
            if(min == u2)
                i2++;
            if(min == u3)
                i3++;
            list.add(min);
        }
        
        return (long)list.get(k);
    }

};
Java Code

总耗时: 557 ms

class Solution:
    """
    @param k: The number k.
    @return: The kth prime number as description.
    """
    def kthPrimeNumber(self, k):
        # write your code here
        i1 = 1
        i2 = 1
        i3 = 1
        u = []
        u.append(1)
        while len(u) <= k:
            u1 = u[i1 - 1] * 3
            u2 = u[i2 - 1] * 5
            u3 = u[i3 - 1] * 7
            minu = min(u1,min(u2,u3))
            if minu == u1:
                i1 += 1
            if minu == u2:
                i2 += 1
            if minu == u3:
                i3 += 1
            u.append(minu)
        return u[k]
Python Code

总耗时: 108 ms

输出第10个丑数的计算过程:

[1]
-------------------
3 5 7
[1, 3]
-------------------
9 5 7
[1, 3, 5]
-------------------
9 15 7
[1, 3, 5, 7]
-------------------
9 15 21
[1, 3, 5, 7, 9]
-------------------
15 15 21
[1, 3, 5, 7, 9, 15]
-------------------
21 25 21
[1, 3, 5, 7, 9, 15, 21]
-------------------
27 25 35
[1, 3, 5, 7, 9, 15, 21, 25]
-------------------
27 35 35
[1, 3, 5, 7, 9, 15, 21, 25, 27]
-------------------
45 35 35
[1, 3, 5, 7, 9, 15, 21, 25, 27, 35]
-------------------
45 45 49
[1, 3, 5, 7, 9, 15, 21, 25, 27, 35, 45]
-------------------
45

原文地址:https://www.cnblogs.com/bbbblog/p/4978237.html