丑数问题及变种小结

来源:互联网 发布:数据产品经理做什么 编辑:程序博客网 时间:2024/05/28 05:15

丑数问题及变种小结

  • 丑数问题及变种小结
    • 声明
    • 判断丑数
    • 找出第k大丑数
      • 丑数推论
      • 1 解法1OnlognOnlog n时间复杂度
      • 2 解法2OnOn时间复杂度
    • 找出第k大自定义丑数
      • 1 解法1Onlog n时间复杂度

声明

文章均为本人技术笔记,转载请注明出处:
[1] https://segmentfault.com/u/yzwall
[2] blog.csdn.net/j_dark/

1 判断丑数

因子只包含2,3,5的数称为丑数(Ugly Number),习惯上把1当做第一个丑数

  • lintcode 517 ugly number
  • 剑指offer 面试题34 丑数

解法:参考剑指offer,将待判断目标依次连续整除2,3,5,如果最后得到1,证明该数为丑数;

/** * 依次整除2,3,5判断(2,3,5顺序判断时间最优) * http://www.lintcode.com/zh-cn/problem/ugly-number/ * 题意:判断一个数是否是丑数 * @author yzwall */class Solution {    public boolean isUgly(int num) {        if (num == 0) {            return false;        }        if (num == 1) {            return true;        }        while (num % 2 == 0) {            num /= 2;        }        while (num % 3 == 0) {            num /= 3;        }        while (num % 5 == 0) {            num /= 5;        }         return num == 1 ? true : false;    }}

2 找出第k大丑数

  • lintcode 4 ugly number ii
  • 剑指offer 面试题34 丑数拓展

丑数推论

根据丑数定义,有推论如下:
任取一个丑数m,记m2=m×2m3=m×3m5=m×5
1. m2m3m5必然是丑数;
2. 如果m为当前第n个丑数,那么m2, m3m5中的最小值必然是第n+1个丑数;

2.1 解法1:O(nlogn)时间复杂度

通过丑数推论,用优先队列PriorityQueue<T>的队首保存当前第n个丑数,用哈希表HashSet<T>保证优先队列中没有重复丑数;

/** * 题意:求第n个丑数 * http://www.lintcode.com/zh-cn/problem/ugly-number-ii/ * 解法1:优先队列+HashSet求解,时间复杂度O(nlogn) * @author yzwall */class Solution13 {    public int nthUglyNumber(int n) {        PriorityQueue<Long> pq = new PriorityQueue<>(n, new Comparator<Long>(){            public int compare(Long o1, Long o2) {                return o1 < o2 ? -1 : 1;            }        });        HashSet<Long> hash = new HashSet<>();        hash.add(1L);        pq.offer(1L);        int[] primes = new int[]{2, 3, 5};        for (int prime : primes) {            hash.add((long)prime);            pq.offer((long)prime);        }        long min = primes[0];        for (int i = 0; i < n; i++) {            // min始终为第i+1个丑数,优先队列提供保证            min = pq.poll();            for (int prime : primes) {                if (!hash.contains(min * prime)) {                    hash.add(min * prime);                    // HashSet保证优先队列中无重复丑数                    pq.offer(min * prime);                }            }        }        return (int)min;    }}

2.2 解法2:O(n)时间复杂度

根据丑数推论,与解法2.1相比,
1. 对于当前第n个丑数m,找到超过m的第一个m2,m3m5,三者之间的最小者必然是第n+1个丑数
2. 用数组保存生成的丑数,避免使用优先队列和哈希表,时间复杂度优化到O(n),空间复杂度仍然为O(n)

代码部分参考剑指offer 面试题34 丑数拓展

3 找出第k大自定义丑数

自定义丑数的定义是正整数并且所有的质数因子都在所给定的一个大小为 k 的质数集合内。
比如指定质数集合为 [2, 7, 13, 19], 那么 [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] 是前12个超级丑数
自定义丑数是广义化的丑数,丑数的质数集合指定为[2, 3, 5]

  • lintcode 518 siper ugly number

3.1 解法1:O(nlogn)时间复杂度

丑数推论,可推广至自定义丑数:
任取一个自定义丑数m,记指定质数集合为primes[], 记mi=m×primes[i],i=0,1,2,...,primes.length1
1. mi必然是自定义丑数;
2. 如果m为当前第n个丑数,那么mi中的最小值必然是第n+1个自定义丑数;

通过上述推论,用优先队列PriorityQueue<T>的队首保存当前第n个自定义丑数,用哈希表HashSet<T>保证优先队列中没有重复自定义丑数;

/** * 题意:求第n个自定义丑数 * http://www.lintcode.com/zh-cn/problem/super-ugly-number/ * 解法1:优先队列+HashSet求解,时间复杂度O(nlogn),空间复杂度O(n) * @author yzwall */class Solution {    public int nthSuperUglyNumber(int n, int[] primes) {        PriorityQueue<Long> pq = new PriorityQueue<>(n, new Comparator<Long>(){            public int compare(Long o1, Long o2) {                return o1 < o2 ? -1 : 1;            }        });        HashSet<Long> hash = new HashSet<>();        hash.add(1L);        pq.offer(1L);        for (int prime : primes) {            hash.add((long)prime);            pq.offer((long)prime);        }        long min = primes[0];        for (int i = 0; i < n; i++) {            min = pq.poll();            for (int prime : primes) {                if (!hash.contains(min * prime)) {                    hash.add(min * prime);                    pq.offer(min * prime);                }            }        }        return (int)min;    }}
原创粉丝点击