leetcode263,264,313 ugly numbers

前言

这一篇博客把ugly numbers系列的题目作一个整理。这三道题正好是一个思路的按部就班,因此放在一篇博客当中。面试

Ugly Number

Write a program to check whether a given number is an ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. 
For example, 6, 8 are ugly while 14 is not ugly since it includes another prime factor 7.

Note that 1 is typically treated as an ugly number.

丑数是指只包含2,3,5质因数的数。所以6,8是丑数由于6=2*3,8=2*2*2,而14不是丑数由于14包含质因数7。如今写一个方法判断一个数字是不是丑数。算法

这题只须要将全部的2,3,5质数消去以后,余下的质数是否是1来进行判断。代码以下:编程

public boolean isUgly(int num) {
        if(num <= 0) return false;
        while(num % 2 == 0) num /= 2;
        while(num % 5 == 0) num /= 5;
        while(num % 3 == 0) num /= 3;
        return num == 1;
    }

264. Ugly Number II

Write a program to find the n-th ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. 
For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.

Note that 1 is typically treated as an ugly number, and n does not exceed 1690.

这道题目在上一题定义的基础上,要找到第n个丑数。数组

这题能够使用暴力遍历法,从1开始,对每个数都进行判断,直到找到第n个丑数为止。可是这样的方法效率不好。因此须要寻找一下这些数之间的规律,从而找到更好的解决方法。微信

这里运用了Dynamic Programming的编程思想来解决。如今咱们能够将丑数完整的纳入如下三类:数据结构

所有丑数: 1, 2, 3, 4, 5, 6, 8...
包含丑数2: 1*2, 2*2, 3*2, 4*2, 5*2, 6*2, 8*2...
包含丑数3: 1*3, 2*3, 3*3, 4*3, 5*3, 6*3, 8*3...
包含丑数5: 1*5, 2*5, 3*5, 4*5, 5*5, 6*5, 7*5...

能够看到,每个丑数子列上,第i位上的值等于所有丑数的第i个丑数*素数。咱们只须要像归并算法中的合并方法那样,将三个子列按照从小到大的状况合并成一个序列便可。ide

咱们从基础状况开始推理,那么每一步的结果以下;this

第0步:spa

所有丑数: 1
包含丑数2: 2
包含丑数3: 3
包含丑数5: 5

此时比较三个子序列,得出最小值为2,则更新所有丑数序列,而且更新包含丑数2的子序列,以下:code

第1步

所有丑数: 1, 2
包含丑数2: 2, 4(2*2)
包含丑数3: 3,
包含丑数5: 5,

再次比较三个子序列,得出最小值为3,更新方法同上,结果以下:
第2步

所有丑数: 1, 2, 3
包含丑数2: 2, 4
包含丑数3: 3, 6(2*3)
包含丑数5: 5

按照如上方法咱们就能够得出第n个丑数的值。 代码以下

public int nthUglyNumber(int n) {
        int idx1 = 0, idx2 = 0, idx3 = 0;
        int[] result = new int[n];
        result[0] = 1;
        for(int i = 1 ; i<n ; i++){
            int num1 = result[idx1] * 2;
            int num2 = result[idx2] * 3;
            int num3 = result[idx3] * 5;
            result[i] = Math.min(num1, Math.min(num2, num3));
            if(num1==result[i]) idx1++;
            if(num2==result[i]) idx2++;
            if(num3==result[i]) idx3++;
        }
        return result[n-1];
    }

313 Super ugly number

Write a program to find the nth super ugly number.

Super ugly numbers are positive numbers whose all prime factors are in the given prime list primes of size k. For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12 super ugly numbers given primes = [2, 7, 13, 19] of size 4.

Note:
(1) 1 is a super ugly number for any given primes.
(2) The given numbers in primes are in ascending order.
(3) 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000.
(4) The nth super ugly number is guaranteed to fit in a 32-bit signed integer.

这一题和上一题的变化相比,基本素数从[2,3,5]变成了任意数量的有序素数构成的数组。其实本质上思路仍是同样的,咱们只须要新建一个数组来存放当前子序列丑数对应的全部丑数的下标就能够了。代码以下:

public int nthSuperUglyNumber(int n, int[] primes) {
        int[] index = new int[primes.length];
        int[] uglyNumbers = new int[primes.length];
        System.arraycopy(primes, 0, uglyNumbers, 0, primes.length);
        
        int[] result = new int[n];
        result[0] = 1;
        for(int i = 1 ; i<n ; i++){
            result[i] = Integer.MAX_VALUE;
            for(int j = 0 ; j<uglyNumbers.length ; j++){
                if(uglyNumbers[j] < result[i]) result[i] = uglyNumbers[j];
            }
            for(int j = 0 ; j <uglyNumbers.length ; j++){
                if(uglyNumbers[j]==result[i]) {
                    index[j]++;
                    uglyNumbers[j] = primes[j] * result[index[j]];
                }
            }
        }
        return result[n-1];
    }

固然,这道题也能够利用特殊的数据结构来完成。优先队列能够很好的知足该状况。咱们先将题目中提供的全部素数输入到优先队列中,至关于存入了全部的子丑数列的第一个元素。所以每一个素数持有的信息包括当前对应的丑数的下标。以后咱们能够将比较的任务交给优先队列的完成,咱们只需从中提取最小的那个数,加到结果集中,同时别忘了更新各个子序列从而消去出现重复值的状况。

代码以下:

public int nthSuperUglyNumberHeap(int n, int[] primes) {
        int[] ugly = new int[n];

        PriorityQueue<Num> pq = new PriorityQueue<>();
        for (int i = 0; i < primes.length; i++) pq.add(new Num(primes[i], 1, primes[i]));
        ugly[0] = 1;

        for (int i = 1; i < n; i++) {
            ugly[i] = pq.peek().val;
            while (pq.peek().val == ugly[i]) {
                Num nxt = pq.poll();
                pq.add(new Num(nxt.p * ugly[nxt.idx], nxt.idx + 1, nxt.p));
            }
        }

        return ugly[n - 1];
    }

    private class Num implements Comparable<Num> {
        int val;
        int idx;
        int p;

        public Num(int val, int idx, int p) {
            this.val = val;
            this.idx = idx;
            this.p = p;
        }

        @Override
        public int compareTo(Num that) {
            return this.val - that.val;
        }
    }

clipboard.png
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注个人微信公众号!将会不按期的发放福利哦~

相关文章
相关标签/搜索