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

时间: 2024-08-05 20:09:28

lintcode 中等题:Ugly Numbers 丑数的相关文章

Humble Numbers(丑数) 超详解!

给定一个素数集合 S = { p[1],p[2],...,p[k] },大于 1 且素因子都属于 S 的数我们成为丑数(Humble Numbers or Ugly Numbers),记第 n 大的丑数为 h[n]. 算法 1: 一种最容易想到的方法当然就是从 2 开始一个一个的判断一个数是否为丑数.这种方法的复杂度约为 O( k * h[n]),铁定超时(如果你这样做而没有超时,请跟 tenshi 联系) 算法 2: 看来只有一个一个地主动生成丑数了 : 我最早做这题的时候,用的是一种比较烂的

DP:Humble Numbers,丑数

描述A number whose only prime factors are 2,3,5 or 7 is called a humble number. The sequence 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 25, 27, ... shows the first 20 humble numbers.  Write a program to find and print the nth elemen

lintcode 中等题: 3 Sum II 三数之和II

题目 三数之和 II 给一个包含n个整数的数组S, 找到和与给定整数target最接近的三元组,返回这三个数的和. 样例 例如S = [-1, 2, 1, -4] and target = 1.  和最接近1的三元组是 -1 + 2 + 1 = 2. 注意 只需要返回三元组之和,无需返回三元组本身 解题 和上一题差不多,程序也只是稍微修改了 public class Solution { /** * @param numbers: Give an array numbers of n integ

lintcode 中等题:find the missing number 寻找缺失的数

题目 寻找缺失的数 给出一个包含 0 .. N 中 N 个数的序列,找出0 .. N 中没有出现在序列中的那个数. 样例 N = 4 且序列为 [0, 1, 3] 时,缺失的数为2. 注意 可以改变序列中数的位置. 挑战 在数组上原地完成,使用O(1)的额外空间和O(N)的时间. 解题 重新定义一个数组存放排序后的数,空间复杂度和时间复杂度都是O(N) public class Solution { /** * @param nums: an array of integers * @retur

lintcode 中等题:Single number III 落单的数III

题目 落单的数 III 给出2*n + 2个的数字,除其中两个数字之外其他每个数字均出现两次,找到这两个数字. 样例 给出 [1,2,2,3,4,4,5,3],返回 1和5 挑战 O(n)时间复杂度,O(1)的额外空间复杂度 解题 根据落单的数I,可以想到,所有的数进行异或运行的结果就是所求两个数的异或结果. 这个异或的结果,二进制数是1的位置说明这两个数对应的二进制位不相同.然后再怎么还原???参考,理解的不是很透,找到第k位后,再判断数组中所以数的第k位是0 还是1,,出现两次的数对求解无影

lintcode 中等题:next permutation下一个排列

题目 下一个排列 给定一个整数数组来表示排列,找出其之后的一个排列. 样例 给出排列[1,3,2,3],其下一个排列是[1,3,3,2] 给出排列[4,3,2,1],其下一个排列是[1,2,3,4] 注意 排列中可能包含重复的整数 解题 和上一题求上一个排列应该很类似 1.对这个数,先从右到左找到递增序列的前一个位置,peakInd 2.若peakInd = -1 这个数直接逆序就是答案了 3.peakInd>= 0 peakInd这个位置的所,和 peakInd 到nums.size() -1

lintcode 中等题:4 Sum 四个数之和

题目 四数之和 给一个包含n个数的整数数组S,在S中找到所有使得和为给定整数target的四元组(a, b, c, d). 样例 例如,对于给定的整数数组S=[1, 0, -1, 0, -2, 2] 和 target=0. 满足要求的四元组集合为: (-1, 0, 0, 1) (-2, -1, 1, 2) (-2, 0, 0, 2) 注意 四元组(a, b, c, d)中,需要满足a <= b <= c <= d 答案中不可以包含重复的四元组. 解题 怎么感觉下面程序已经没法修改了但是在

lintcode 中等题:2 Sum 两个数的和

题目 两数之和 给一个整数数组,找到两个数使得他们的和等于一个给定的数target. 你需要实现的函数twoSum需要返回这两个数的下标, 并且第一个下标小于第二个下标.注意这里下标的范围是1到n,不是以0开头. 样例numbers=[2, 7, 11, 15], target=9 return [1, 2] 注意你可以假设只有一组答案. 解题 题目之前做LeetCode时候写过这个,利用这里利用ArrayList,当 target- numbers[i] 不在 list中,把numbers[i

lintcode 中等题:maximum subarray difference 最大子数组差

题目 最大子数组差 给定一个整数数组,找出两个不重叠的子数组A和B,使两个子数组和的差的绝对值|SUM(A) - SUM(B)|最大. 返回这个最大的差值. 样例 给出数组[1, 2, -3, 1],返回 6 注意 子数组最少包含一个数 挑战 时间复杂度为O(n),空间复杂度为O(n) 解题 刚做了数组中两个子数组和的最大值,这一题是求差,感觉上题的求解思想应该是可以用的 A B 分别是两个子数组的和,则: 所以 当A>B 的时候A越大越好 B越小越好 当A<B 的时候B越大越好 A越小越好