首页 > 试题广场 >

丑数

[编程题]丑数
  • 热度指数:586147 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第 n个丑数。

数据范围:
要求:空间复杂度 , 时间复杂度
示例1

输入

7

输出

8
推荐
class Solution {
public://别人的代码就是精简,惭愧啊,继续学习。
    int GetUglyNumber_Solution(int index) {
		if (index < 7)return index;
		vector<int> res(index);
		res[0] = 1;
		int t2 = 0, t3 = 0, t5 = 0, i;
		for (i = 1; i < index; ++i)
		{
			res[i] = min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5));
			if (res[i] == res[t2] * 2)t2++;
			if (res[i] == res[t3] * 3)t3++;
			if (res[i] == res[t5] * 5)t5++;
		}
		return res[index - 1];
    }
};

编辑于 2019-01-10 19:24:15 回复(145)
public int GetUglyNumber_Solution (int index) {
    // write code here
    if(index<=6){
        return index;
    }
    int x=0 ,y=0 ,z=0;
    int[] res=new int[index];
    res[0]=1;
    for(int i=1;i<index;i++){
        // 1做初始化,后续按×2 3 5取最小值,命中的数角标+1
        res[i]=Math.min(res[x]*2 ,Math.min(res[y]*3 ,res[z]*5));
        if(res[i]==res[x]*2){
            x++;
        }
        if(res[i]==res[y]*3){
            y++;
        }
        if(res[i]==res[z]*5){
            z++;
        }
    }
    return res[index-1];
}

编辑于 2024-03-24 13:17:35 回复(0)
import java.util.*;
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index == 0) return 0;
        int[] prime = {2,3,5};
        PriorityQueue<Long> q = new PriorityQueue<>();
        Set<Long> set = new HashSet<>();
        
        
        long num = 1l;
        q.offer(num);
        while(index -- > 0) {
            num = q.poll();
            for(int p : prime) {
                if(!set.contains(p * num)) {
                    set.add(p * num);
                    q.offer(p * num);
                }
            }
        }
        return (int)num;
    }
}

发表于 2022-09-03 14:04:53 回复(0)
import java.util.*;
public class Solution {
    
    public int GetUglyNumber_Solution(int index) {
        List<Integer> list1=new ArrayList<Integer>();//存储素数
        List<Integer> list2=new ArrayList<Integer>();//存储丑数
        int i=1;
        while(true){
            getSubNum(i,list1,list2);
            if(list2.size()==index){
                return list2.get(index-1);
            }
            i++;
        }

    }
    public void getSubNum(int num,List<Integer> list1,List<Integer> list2){
        if(num==1){
            list1.add(num);
            list2.add(num);
            return;
        }
        int div_num=num/2;
        Set<Integer> set=new HashSet<Integer>();
        for(int i=1;i<=num;i++){
            int tmp1=num/i;
            int tmp2=num%i;
            if(tmp2==0){
                set.add(tmp1);
                set.add(num/i);
            }
        }
        if(set.size()==2){
            list1.add(num);
        }
        for(int n:list1){
            if(set.contains(n)&&n!=1&&n!=2&&n!=3&&n!=5){
                return;
            }
        }
        list2.add(num);
        
    }
}
时间复杂度有点高,最后一个测试用例没通过
发表于 2022-08-30 14:12:08 回复(0)
动态规划思想:每个丑数都是跟2,3,5相乘所得到的,就是后面的需要前面的数字来做铺垫
import java.util.ArrayList;
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        //动态规划
        int[] dp=new int[index+1];
        if(index==0) return 0;
        dp[1]=1;
        int p1=1,p2=1,p3=1;
        for (int i = 2; i <=index; i++) {
            int num1=dp[p1]*2,num2=dp[p2]*3,num3=dp[p3]*5;
            dp[i]=Math.min(num1,Math.min(num2,num3));
            if(dp[i]==num1) p1++;
            if(dp[i]==num2) p2++;
            if(dp[i]==num3) p3++;
        }
        return dp[index];
    }
}


发表于 2022-06-21 17:00:19 回复(0)
import java.util.*;

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if (index <= 6) {
            return index;
        }
        HashSet<Long> set = new HashSet<>();
        PriorityQueue<Long> pq = new PriorityQueue<>();
        long res = 0;
        int[] BASES = {2, 3, 5};
        set.add(1L);
        pq.offer(1L);
        for (int i = 0; i < index; i++) {
            res = pq.poll();
            for (int j = 0; j < BASES.length; j++) {
                long num = res * BASES[j];
                if (!set.contains(num)) {
                    set.add(num);
                    pq.offer(num);
                }
            }
        }
        return (int)res;
    }
}

发表于 2022-06-14 15:37:45 回复(0)
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        // 三指针:
        
        // 边界值
        if(index==0){
            return 0;
        }
        // 定义数组
        int arr[] = new int[index];
        // 分别记录x2 x3 x5的索引位置
        int i2=0;
        int i3=0;
        int i5=0;
        // 规定第一个为1;
        arr[0]=1;
        // 每轮循环产生一个丑数
        for(int i=1;i<index;i++){
            arr[i] = Math.min(arr[i2]*2, Math.min(arr[i3]*3,arr[i5]*5));
            
            // 哪个索引对应值被选中成为下一个丑数,哪个索引就+1
            if(arr[i]==arr[i2]*2)
                i2++;
            if(arr[i]==arr[i3]*3)
                i3++;
            if(arr[i]==arr[i5]*5)
                i5++;
        }
        
        return arr[index-1];
    }
}
看了题解发现和自己最初理解的不太一样,三个指针各自只分别负责x2 x3 x5三路,走了哪路哪个指针就+1,这样保证每个丑数都会被x2 x3 x5各一次。另外注意不能用else if,因为需要排除在同一轮命中2个指针的情况,比如亲测又一轮i2指向3,i3指向2,算出来都是6,三个if分别判定可以避免记录2次6
发表于 2022-02-01 17:32:30 回复(0)
import java.util.*;

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if (index == 0){
            return 0;
        }
        ArrayList<Integer> list = new ArrayList<>();
        int i2 = 0, i3 = 0, i5 = 0;
        list.add(1);
        for (int i = 1; i < index; i++){
            list.add(Math.min(list.get(i2)*2, Math.min(list.get(i3)*3, list.get(i5)*5)));
            if (list.get(i) == list.get(i2)*2)
                i2++;
            if (list.get(i) == list.get(i3)*3)
                i3++;
            if (list.get(i) == list.get(i5)*5)
                i5++;
        }
        return list.get(index-1);
    }
}

发表于 2021-12-21 22:32:25 回复(0)
/*
丑数

后面的丑数由前面的丑数*2,*3,*5的最小值生成。
与斐波那契数列一样,一个一个往后生成。

第0个丑数应该返回0,不知道为什么。
*/
    /*
    思路:
    后面的丑数由前面的丑数*2,*3,*5的最小值来生成。
    与斐波那契数列一样,都是一个一个往后面生成的。
    */
    public int GetUglyNumber_Solution(int index) {
        //第0个丑数是0,不知道为什么
        if(index <= 0) return 0;
        /*需要额外的空间,省不掉
        不记录无法得到想要的值
        */
        ArrayList<Integer> list = new ArrayList<>();
        int i2 = 0,i3 = 0,i5 = 0;
        list.add(1);
        //自己举一个n=3的例子
        while(list.size() < index){
            int v2 = list.get(i2) * 2;
            int v3 = list.get(i3) * 3;
            int v5 = list.get(i5) * 5;
            //取最小值可以两个两个取
            int minVal = Math.min(v2,Math.min(v3,v5));
            list.add(minVal);
            //i只要与最小值相等就右移(可能会有多个进行移动)
            if(minVal == v2) i2++;
            if(minVal == v3) i3++;
            if(minVal == v5) i5++;
        }
        return list.get(list.size()-1);
    }


发表于 2021-08-27 15:09:30 回复(0)
import java.util.*;
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index<=0) return 0;
        
        int[] arr = new int[index];
        arr[0] = 1;
        
        int loc2 = 0;
        int loc3 = 0;
        int loc5 = 0;
        int cur;
        for(int i =1;i<index;++i){
            cur = arr[i-1];
            while(arr[loc2]*2<=cur) loc2++;
            while(arr[loc3]*3<=cur) loc3++;
            while(arr[loc5]*5<=cur) loc5++;
            arr[i] = Math.min(Math.min(arr[loc2]*2,arr[loc3]*3),arr[loc5]*5);
        }
        return arr[index-1];
    }
}

发表于 2021-06-03 07:11:05 回复(0)
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index==0)return 0;
        int[] res=new int[index];
        int t2=0,t3=0,t5=0;
        res[0]=1;
        for(int i=1;i<index;i++){
           res[i]=Math.min(Math.min(res[t2]*2,res[t3]*3),res[t5]*5);
           if(res[i]==res[t2]*2) t2++;
           if(res[i]==res[t3]*3) t3++;
           if(res[i]==res[t5]*5) t5++;
         }
         return res[index-1];
    }
}
丑数的定义是1或者因子只有2 3 5,可推出丑数=丑数*丑数,假定丑数有序序列为:a1,a2,a3.......an
所以可以将以上序列(a1除外)可以分成3类,必定满足:
包含2的有序丑数序列:2*a1, 2*a2, 2*a3 .....
包含3的有序丑数序列:3*a1, 3*a2, 3*a3 .....
包含5的有序丑数序列:5*a1, 5*a2, 5*a3 .....
以上3个序列的个数总数和为n个,而且已知a1 = 1了,将以上三个序列合并成一个有序序列即可 
发表于 2021-04-14 21:16:34 回复(0)
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index==0) return 0;
        int i2=0,i3=0,i5=0;
        int x2,x3,x5;
        int []dp=new int[index];
        dp[0]=1;
        for(int i=1;i<index;i++){
            x2=dp[i2]*2;
            x3=dp[i3]*3;
            x5=dp[i5]*5;//在上一次的基础上累乘
            dp[i]=Math.min(x2,Math.min(x3,x5));//最小值为下一个丑数
            if(dp[i]==x2) i2++;
            if(dp[i]==x3) i3++;
            if(dp[i]==x5) i5++;
        }
        return dp[index-1];
    }
}

发表于 2021-03-31 15:39:10 回复(0)
import java.util.*;
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if (index <= 1) {
            return index;
        }
        int a = 1,
            b = 1,
            c = 1;
        int[] dp = new int[index + 1];
        dp[1] = 1;
        for (int i = 2; i <= index; i++) {
            int n1 = dp[a] * 2,
                n2 = dp[b] * 3,
                n3 = dp[c] * 5;
            dp[i] = Math.min(n1, Math.min(n2, n3));
            if (dp[i] == n1) {
                a++;
            }
            if (dp[i] == n2) {
                b++;
            }
            if (dp[i] == n3) {
                c++;
            }
        }
        return dp[index];
    }
}
动态规划法:找出状态转移方程:
dp[i] = min(dp[a], dp[b], dp[c]);
下一个丑数一定等于之前某一丑数乘以2,3,5且恰好大于当前丑数
由于要保证找出的丑数是从小到大有序的
则必须在*2,*3,*5三个结果中取最小的
同时更新*2,*3,*5恰好大于当前丑数的丑数下标
发表于 2021-02-23 17:03:28 回复(0)
用动态规划还是简单些
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index <= 0) return 0;
        if(index == 1) return 1;
        int[] ret = new int[index];
        ret[0] = 1;
        int t2 = 0, t3 = 0, t5 = 0;
        for(int i = 1; i < index; i++){
            ret[i] = Math.min(ret[t2]*2, Math.min(ret[t3]*3,ret[t5]*5));
            if(ret[i] == ret[t2]*2) t2++;
            if(ret[i] == ret[t3]*3) t3++;
            if(ret[i] == ret[t5]*5) t5++;
        }
        return ret[index-1];
    }
}


发表于 2021-02-03 22:34:05 回复(0)
import java.util.*;
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index <= 0)
            return 0;
        int p2=0,p3=0,p5=0;//初始化三个指向三个潜在成为最小丑数的位置
        int[] result = new int[index];
        result[0] = 1;//
        for(int i=1; i < index; i++){
            result[i] = Math.min(result[p2]*2, Math.min(result[p3]*3, result[p5]*5));
            if(result[i] == result[p2]*2)p2++;//为了防止重复需要三个if都能够走到
            if(result[i] == result[p3]*3)p3++;//为了防止重复需要三个if都能够走到
            if(result[i] == result[p5]*5)p5++;//为了防止重复需要三个if都能够走到
 
 
        }
        return result[index-1];
        
    }
}

三个指针的动态规划,
因为相等的时候肯定是乘2最小,但是存在x<y<z的情况。
分别用三个指针指代乘2,乘3,乘5的情况
开始时三个指针相等,返回最小值
对应的指向最小值的指针加加前进一位
发表于 2021-01-28 21:40:19 回复(0)
import java.util.*;

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        PriorityQueue<Long> queue = new PriorityQueue<>();
        Map<Long, Integer> map = new HashMap<>();
        int count = 0;
        long ugly = 0l;
        queue.offer(1l);
        while (count < index) {
            ugly = queue.poll();
            if (!map.containsKey(ugly)) {
                count++;
                map.put(ugly, 0);
                queue.offer(ugly * 2);
                queue.offer(ugly * 3);
                queue.offer(ugly * 5);
            }
        }
        return (int) ugly;
    }
}
发表于 2020-10-20 10:39:39 回复(0)
import java.lang.*;

//思路:一个丑数成以2/3/5,得到的还是一个丑数;有3个对列pos2/pos3/pos5,每次都取最小的数,放到数组中【小于7的数都是丑数】。
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        //小于7的数都是丑数
        if(index < 7){
            return index;
        }
        
        int[] result = new int[index];
        //1是第一个丑数
        result[0] = 1;
        
        //有3个对列pos2/pos3/pos5,每次都取最小的数,放到数组中
        int pos2=0,pos3=0,pos5=0;
        
        //因为1已经是第一个丑数了,所以这里i从1开始
        for(int i=1;i<index;i++){
            
            
            int a = result[pos2] * 2;
            int b = result[pos3] * 3;
            int c = result[pos5] * 5;
            //实现Math.min(a,b,c)的效果
            result[i] = Math.min(a,Math.min(b,c));
            
            if(result[i] == a){//为了防止重复需要三个if都能够走到
                pos2++;
            }
            
            if(result[i] == b){//为了防止重复需要三个if都能够走到
                pos3++;
            }
            if(result[i] == c){//为了防止重复需要三个if都能够走到
                pos5++;
            }
        }
        
        return result[index-1];
    }
}

发表于 2020-07-30 11:31:16 回复(0)

使用数组记录,依次添加下一个丑数进数组;
下一个丑数计算:用base2、base3、base5记录三个基数的数组下标,ugly2、ugly3、ugly5分别表示对应基数x2、x3、x5的结果(候选丑数),下一个丑数一定是比当前最后一个丑数大的,所以如果ugly2、ugly3、ugly5如果小于等于当前最后一个丑数,需先移动下标位置重新计算,预处理后下一个丑数即ugly2、ugly3、ugly5三个候选值中的最小值

public static int GetUglyNumber_Solution(int index){
        if(index<=1){
            return index;
        }
        int[] array = new int[index];
        array[0]=1;
        int lastI=1;
        int base2=0,base3=0,base5=0;
        int ugly2=array[base2]*2,ugly3=array[base3]*3,ugly5=array[base5]*5;
        while (lastI<index){
            int max = array[lastI-1];
            while (ugly2<=max){
                base2++;
                ugly2=array[base2]*2;
            }
            while (ugly3<=max){
                base3++;
                ugly3=array[base3]*3;
            }
            while (ugly5<=max){
                base5++;
                ugly5=array[base5]*5;
            }
            array[lastI]=getMin(ugly2,ugly3,ugly5);
            System.out.println("lastI="+lastI+",ugly="+ugly2+","+ugly3+","+ugly5+"==>"+array[lastI]);
            lastI++;
        }
        return array[index-1];
    }
    public static int getMin(int a,int b,int c){
        int min=a;
        if(min>b){
            min=b;
        }
        if(min>c){
            min=c;
        }
        return min;
    }
发表于 2020-07-29 12:08:12 回复(0)
/*
i>1,第i个丑数必定可分解为2*a或3*b或5*c的形式,必定是由2*a或3*b或5*c而来。
而a,b,c也是i前面的丑数。a前面的丑数,必定已经乘过2得到过丑数。

*/
public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if (index <= 0) {
            return 0;
        }
        int[] u = new int[index];
        u[0] = 1;
        int p2 = 0;
        int p3 = 0;
        int p5 = 0;
        for (int i=1;i<index;i++) {
            u[i] = Math.min(u[p2]*2, Math.min(u[p3]*3, u[p5]*5));
            if (u[i] == u[p2]*2) {
                p2++;
            } 
            if (u[i] == u[p3]*3) {
                p3++;
            } 
            if (u[i] == u[p5]*5){
                p5++;
            }
        }
        return u[index-1];
    }
}

编辑于 2020-07-10 20:42:15 回复(0)
楼里大神多,我这菜鸟也贴出来献丑了,代码在eclipse上运行没错,但是超时了。。。。
public static int GetUglyNumber_Solution(int index) {
	        if(index == 1)
	            return 1;
	        int i = 2;
	        int prime = 1;
	        while(i <= index){
	            prime++;
	            int temp = prime;
	            while(temp != 1){
	                if(temp%2 == 0){
	                    temp /= 2;
	                }else if(temp%3 == 0){
	                    temp /= 3;
	                }else if(temp%5 == 0){
	                    temp /= 5;
	                }else{
	                    temp = prime + 1;
	                    prime ++;
	                }
	                
	            }
	            
	            i++;
	            
	        }
	        return prime;
	    }


发表于 2020-07-02 22:09:22 回复(0)

看了三个答案,总结一下。。。

**
**

首先从丑数的定义我们知道,一个丑数的因子只有2,3,5,那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z,换句话说一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到,那么我们从1开始乘以2,3,5,就得到2,3,5三个丑数,在从这三个丑数出发乘以2,3,5就得到4,6,10,6,9,15,10,15,25九个丑数,我们发现这种方*得到重复的丑数,而且我们题目要求第N个丑数,这样的方法得到的丑数也是无序的。那么我们可以维护三个队列:**

(1)丑数数组: 1

乘以2的队列:2

乘以3的队列:3

乘以5的队列:5

选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以**2,3,5放入三个队列;**

(2)丑数数组:1,2

乘以2的队列:4

乘以3的队列:3,6

乘以5的队列:5,10

选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以**2,3,5放入三个队列;**

(3)丑数数组:1,2,3

乘以2的队列:4,6

乘以3的队列:6,9

乘以5的队列:5,10,15

选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以**2,3,5放入三个队列;**

(4)丑数数组:1,2,3,4

乘以2的队列:6,8

乘以3的队列:6,9,12

乘以5的队列:5,10,15,20

选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以**2,3,5放入三个队列;**

(5)丑数数组:1,2,3,4,5

乘以2的队列:6,8,10,

乘以3的队列:6,9,12,15

乘以5的队列:10,15,20,25

选择三个队列头里最小的数6加入丑数数组,但我们发现,有两个队列头都为6,所以我们弹出两个队列头,同时将12,18,30放入三个队列;

……………………

疑问:

1.为什么分三个队列?

丑数数组里的数一定是有序的,因为我们是从丑数数组里的数乘以2,3,5选出的最小数,一定比以前未乘以2,3,5大,同时对于三个队列内部,按先后顺序乘以2,3,5分别放入,所以同一个队列内部也是有序的;

2.为什么比较三个队列头部最小的数放入丑数数组?

因为三个队列是有序的,所以取出三个头中最小的,等同于找到了三个队列所有数中最小的。

实现思路:

我们没有必要维护三个队列,只需要记录三个指针显示到达哪一步;“|”表示指针,arr表示丑数数组;

(1)1

|2

|3

|5

目前指针指向0,0,0,队列头arr[0] * 2 = 2, arr[0] * 3 = 3, arr[0] * 5 = 5

(2)1 2

2 |4

|3 6

|5 10

目前指针指向1,0,0,队列头arr[1] * 2 = 4, arr[0] * 3 = 3, arr[0] * 5 = 5

(3)1 2 3

2| 4 6

3 |6 9

|5 10 15

目前指针指向1,1,0,队列头arr[1] * 2 = 4, arr[1] * 3 = 6, arr[0] * 5 = 5

 public int GetUglyNumber_Solution(int n)     {         if(n0)return 0;         ArrayListInteger> list=new ArrayListInteger>();         list.add(1);         int i2=0,i3=0,i5=0;         while(list.size()n)//循环的条件         {             int m2=list.get(i2)*2;             int m3=list.get(i3)*3;             int m5=list.get(i5)*5;             int min=Math.min(m2,Math.min(m3,m5));             list.add(min);             if(min==m2)i2++;             if(min==m3)i3++;             if(min==m5)i5++;         }         return list.get(list.size()-1);     }

所有的丑数分为三种类型 2i,3i,5*i 其中 i是数组中的元素,一开始只有1

2*1 31 51

22 *31* 51 *22* 32 51 23 32 5*1 2*3 32 52 2*4 33 52 25 *33* 52 *25* 34 52 2*6 34 53 28 *35* 53 *28* 36 54

发表于 2020-05-20 10:39:46 回复(0)

问题信息

难度:
108条回答 133915浏览

热门推荐

通过挑战的用户

查看代码