面试题49. 丑数(Java)(三指针,动态规划)(优先队列)

1 题目

我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

示例:

输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:

1 是丑数。
n 不超过1690。
注意:本题与主站 264 题相同:https://leetcode-cn.com/problems/ugly-number-ii/

2 Java

2.1 方法一(三指针,动态规划)

核心:新丑数(大丑数)都是由2/3/5再乘一个小丑数得到
创建三个指针指向丑数列表,分别作为2/3/5的索引,指向各自的小丑数

class Solution {
    public int nthUglyNumber(int n) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        if(n < 6)   return list.get(n - 1);

        // 新的丑数都是由2/3/5再乘一个小丑数构成
        // indexA/B/C 小丑数的索引
        // valueA/B/C 新丑数候选值,新丑数将从中诞生
        // 选其中最小值先加入list

        int indexA = 2, indexB = 1, indexC = 1;
        while(list.size() < n){
            int valueA = 2*list.get(indexA), valueB = 3*list.get(indexB), valueC = 5*list.get(indexC);
            // 若新丑数中存在相等值,将索引向后移
            if(valueA == valueB && valueA == valueC){
                indexB++;
                indexC++;
            }
            else if(valueA == valueB)   indexB++;
            else if(valueA == valueC)   indexC++;
            else if(valueB == valueC)   indexC++;

            // 从新丑数候选值中,选最小值加入list
            valueA = 2*list.get(indexA); valueB = 3*list.get(indexB); valueC = 5*list.get(indexC);
            if(valueA < valueB && valueA < valueC){
                list.add(valueA);
                indexA++;
            }
            else if(valueB < valueA && valueB < valueC){
                list.add(valueB);
                indexB++;
            }
            else if(valueC < valueA && valueC < valueB){
                list.add(valueC);
                indexC++;
            }
        }

        return list.get(n - 1);
    }
}

进行优化
1 不需要先判断是否存在相等值,再加入list
2 也无需使用list,长度n已知

class Solution {
    public int nthUglyNumber(int n) {

        // 新的丑数都是由2/3/5再乘一个小丑数构成;即后面的结果与前面有关

        // 创建备忘录
        int[] dp = new int[n];
        // 备忘录初始化
        dp[0] = 1;

        // 向前步进
        int i2 = 0, i3 = 0, i5 = 0;
        for(int i = 1; i < n; i++){
            dp[i] = Math.min(Math.min(2*dp[i2], 3*dp[i3]), 5*dp[i5]);
            if(2*dp[i2] == dp[i])    i2++;
            if(3*dp[i3] == dp[i])    i3++;
            if(5*dp[i5] == dp[i])    i5++;
        }

        return dp[n - 1];
    }
}

2.2 方法二(优先队列)

应该还能优化,queue里存的太多了,不止n-1个丑数

class Solution {
    public int nthUglyNumber(int n) {

        // HashSet和PriorityQueue同步更新,set用于查重,queue用于得到当前队列最大值
        HashSet<Long> set = new HashSet<>();
        PriorityQueue<Long> queue = new PriorityQueue<>();
        int[] ans = new int[n];
        int[] select = {2, 3, 5};
        ans[0] = 1; set.add(1L); queue.add(1L);
        
        for(int i = 0; i < n; i ++){
            long oldUgly = queue.poll();
            ans[i] = (int)(oldUgly);
            for(int k: select){
                if(!set.contains(k * oldUgly)){
                    set.add(k * oldUgly);
                    queue.add(k * oldUgly);
                }
            }
        }

        return ans[n - 1];
    }
}
发布了131 篇原创文章 · 获赞 0 · 访问量 2284

猜你喜欢

转载自blog.csdn.net/weixin_43969686/article/details/105046823
今日推荐