醜數問題及變種小結

醜數問題及變種小結

聲明

文章均爲本人技術筆記,轉載請註明出處:
[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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章