剑指offer三十三--第n个丑数

前言:本文只提供了解题的思路,代码只有想法二的,但想法二不是最优解。如果你想找一个最好答案,可能本文不合适。如果你想一步一步接近最优答案的话,可以耐心看完。

题目描述

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
前20个丑数为:1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 36。

解题思路

想法一

写一个判断是否为丑数的函数,这个函数代码依次将2,3,5除尽。若最后为1则是丑数
从1往后找,找满n个丑数为止
代码如下

bool PanDuan(int val) {
		if (val == 1)
			return true;

		// 把2的因子除尽
		while (val % 2 == 0) {
			val /= 2;
		}
		// 把3的因子
		while (val % 3 == 0) {
			val /= 3;
		}
		// 5
		while (val % 5 == 0) {
			val /= 5;
		}

		if (val == 1)
			return true;
		else
			return false;
	}

  

想法二

第i个丑数由前面i-1个数中的某个数 乘以 2 3 5得到,我们找的乘后的数要大于第i-1个丑数,且尽可能的小
那求第i个丑数,将前面的i-1个数依次遍历,遍历一个数时 让其 乘以2 3 5;
结果 要大于第i-1个整数,且尽可能的小。
由前面的东西给推出后面,是一种动态规划的写法,但存在重复计算。
代码见最后;

想法三

方法2存在重复计算 算3要遍历1、2所有 算4 还是要遍历1、2.可以想办法把计算结果保存起来能起来。具体来说 丑数数组A 存放前i-1个丑数数组(没有钱i-1个丑数) 乘以2 3 5后容器B
从B中取最小的数x加入到A中,然后取出的数x乘以2 3 5放入到容器B中
如此反复直到丑数数组A数量达到n
对于容器B 有如下两种方式

容器:一个set

保存方式由两种建一个set 存由已知丑数得到的结果,然后取set最小的值为下一个丑数 乘以2 3 5得到三个数加入set中
缺点是每次加入三个数后都要重新排序,因为顺序不固定

容器:三个队列

保存方式2创建3个队列 乘以2 3 5所得数列,这样避免了重新排序,具体见实例
丑数数组;
(1)丑数数组A1
乘以2队列|2
乘以3队列|3
乘以5队列|5
(2)丑数数组1 2
乘以2 |4
乘以3|3 6
乘以5|5 10
(3)丑数数组1 2 3
乘以2| 4 6
乘以3 |6 9
乘以5|5 10 15

代码C++

想法二

class Solution {
public:	
    int GetUglyNumber_Solution(int index) {
		if (index == 1 || index == 2 || index == 3)
			return index;
		vector<int> uglys;
		uglys.push_back(1);
		
		for (int i = 1; i < index; i++) {//依次计算出第i个丑数,i是下标
			int min = INT32_MAX; //记录由前面的ugly获取第i个丑数的最小值
			for (int j = i - 1; j >= 0; j--) {
				if (uglys[j] * 2 > uglys[i - 1] && uglys[j] * 2 < min) {
					min = uglys[j] * 2;
				}
				else if (uglys[j] * 3 > uglys[i - 1] && uglys[j] * 3 < min) {
					min = uglys[j] * 3;
				}
				else if(uglys[j] * 5 > uglys[i - 1] && uglys[j] * 5 < min) {
					min = uglys[j] * 5;
				}			
			}
			uglys.push_back(min);
		}
		return uglys[index - 1];
	}
};

  

原文地址:https://www.cnblogs.com/linxuesong/p/12214629.html