剑指offer学习笔记 丑数

面试题49:丑数。我们把只包含因子2、3、5的数称为丑数。求按从小到大的顺序的第1500个丑数。如6、8是丑数,但14不是,因为14包含因子7,习惯上我们把1当做第一个丑数。

根据丑数定义,丑数只能被2、3、5整除,即,如一个数能被2(3或5)整除,就连续除以2(3或5),如果最后得到的是1,那么这个数就是丑数,否则不是:

bool isUgly(int num) {
    while (num % 2 == 0) {
        num /= 2;
    }
    while (num % 3 == 0) {
        num /= 3;
    }
    while (num % 5 == 0) {
        num /= 5;
    }
    return (number == 1) ? true : false;
}

然后对每个数字计算,但这种方法时间效率不高,是不是丑数都要进行计算。

我们可以只对丑数进行计算,根据丑数定义,丑数应该是另一个丑数乘2、3或5的结果(1除外),因此我们可以创建一个数组,里面都是排好序的丑数,每个丑数都是前面丑数乘2、3或5得到的。

这种思路关键在于如何确保数组里的丑数都是排好序的,假设数组中已经有若干个排好序的丑数,并且把已有最大丑数记作M,下一个丑数一定是前面某一个丑数乘2、3或5的结果,我们首先把已有丑数乘2,我们会得到若干个小于M的结果,由于是按顺序生成的,因此小于等于M的丑数一定已经在数组里了,我们不需再考虑,另外还会得到若干个大于M的结果,但我们只需要取第一个大于M的结果即可,记为M2。以同样结果我们得到M3和M5,然后比较这三个数,找到最小的即为下一个丑数。

前面分析时,每次都从头到尾找到M2、M3、M5,这是不必要的,我们只需要把求出M2的这个位置T2记下来,下次继续从此处开始即可:

#include <iostream>
#include <vector>
using namespace std;

int GetUglyNum(int index) {
	if (index < 1) {
		return 0;
	}

	vector<int> uglys = { 1 };
	int uglyNum = 1;
	int T2 = 0, T3 = 0, T5 = 0;

	while (uglyNum < index) {
		int minT = min(min(uglys[T2] * 2, uglys[T3] * 3), uglys[T5] * 5);    //找到比当前最大的丑数后面的丑数
		uglys.push_back(minT);

		while (uglys[T2] * 2 <= uglys[uglyNum]) {    //当<=时,说明uglys里已经有这个数了
			++T2;
		}
		while (uglys[T3] * 3 <= uglys[uglyNum]) {
			++T3;
		}
		while (uglys[T5] * 5 <= uglys[uglyNum]) {
			++T5;
		}
		++uglyNum;
	}

	int res = uglys[uglyNum - 1];

	return res;
}

int main() {
	cout << GetUglyNum(1500) << endl;    //打印859963392
}

第二种思路不需要在非丑数的整数上进行任何计算,但需要保存已经生成的丑数,因此需要一个数组,来容纳1500个丑数,占用1500x4字节的内存,相当于用空间换时间。

发布了211 篇原创文章 · 获赞 11 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/tus00000/article/details/105076779