剑指Offer:丑数

我们把只包含因子 2、3 和 5 的数称作丑数 (Ugly Number)。求按从小 到大的顺序的第 1500 个丑数。例如 6、8都是丑数,但 14 不是,它包含因子 7。习惯上我们把 1当做第一个丑数。

解法一:逐一判断是否是丑数,简单但是不够高效

      数字n是数字m的因子说明m%n==0。丑数的因子只有2、3和5,说明一个数number如果能被2整除我们就将它连续除2;如果能被3整除我们就连续除以3;如果能被5整除我们就连续除以5。如果最后number==1,说明这个数是丑数,否则不是。

实现代码:

//判断一个数是否为丑数
private static boolean isUglyNum(long num){
	while(num%2==0){
		num /= 2;
	}
	while(num%3==0){
		num /= 3;
	}
	while(num%5==0){
		num /= 5;
	}
	if(num==1){
		return true;
	}
	return false;
}

private static long GetUglyNum(int counts){
	if(counts<=0){
		return 0;
	}
	int index = 0;
	long num = 0;
	while(index<counts){
		num++;
		if(isUglyNum(num)){
			index++;
		}
	}
	return num;
}

为了获取第1500个丑数我们所要验证的数字数量远远大于1500,有太多的非丑数验证,这样的效率太低下了。

解法二:创建数组存放丑数,通过已有丑数产生新的丑数

      我们不要把精力花费在验证是否是丑数上,我们应该考虑如何生成丑数。有第一个丑数1,那么将这个丑数分别*2、*3、*5得到丑数2、3、5,再分别*2、*3、*5得到(4、6、10) *2、(6、9、15) *3、(10、15、25) *5,我们需要考虑去重,但是最困难的是要将这个数组中的丑数进行有小到大的排序,譬如找第10个丑数12,可是就这个方法来说去重后第10个丑数是25,还要进行下一轮的乘法才能得到12。我们要改进。

      这个方法的关键之处在于我们要按照从小到大的顺序生成丑数。只有这样我们才能获得指定的第几个丑数。假设数组中已经有若干个丑数排好序后存放在数组中 ,并且把己有最大的丑数记做M,我们接下来分析如何生成下一个丑数 。该丑数肯定是前面某一个丑数乘以2、3 或者 5 的结果,所以我们首先考虑把已有的每个丑数乘以2。在乘以2 的时候,能得到若干个小于或等于 M 的结果。由于是按照顺序生成的, 小于或者等于 M 肯定己经在数组中了,我们不需再次考虑:还会得到若干 个大于 M 的结果,但我们只需要第一个大于 M 的结果,因为我们希望丑数是按从小到大 的顺序生成的,其他更大的结果以后再说。我们把得到的第一个乘以2 后大于 M 的结果记为 M2。 同样,我们把已有的每一个丑数乘以3 和 5,能得到第一个大于 M 的结果 M3 和 M5 那么下一个丑数应该是 M2、M3 和 M5 这 3 个数的最小者。


      我们还需要纪录乘以2、乘以3和乘以5得来的最大丑数的下标。拿乘以2得到的丑数最大值为例。对于最大丑数之前的丑数满足这样的规则:之前的丑数*2都小于等于最大丑数。一个循环就可确定乘以2得来的最大丑数的位置。乘以3和乘以5得来的最大丑数位置类似。

实现代码:

private static long GetUglyNum(int index){
	if ( index <=  0 ) return 0 ;
	long[] uglyNums = new long[index];
	uglyNums[0]=1;
	int nextUglyindex = 1;
	
	int  pos2 = 0; 
	int  pos3 = 0; 
	int  pos5 = 0;
	
	while ( nextUglyindex < index){
		//下一个丑数应该是 M2、M3 和 M5 这 3 个数的最小者。
		long min = Min(uglyNums[pos2]*2,uglyNums[pos3]*3,uglyNums[pos5]*5); 
		uglyNums[nextUglyindex]= min;
		//找到2、3、5对应最大丑数位置
		while ( uglyNums[pos2] * 2 <= uglyNums[nextUglyindex])
			++pos2;
		while ( uglyNums[pos3] * 3 <= uglyNums[nextUglyindex])
			++pos3;
		while ( uglyNums[pos5] * 5 <= uglyNums[nextUglyindex])
			++pos5;
		++nextUglyindex;
	}
	
	long ugly = uglyNums[nextUglyindex-1];
	return  ugly;	
}

private static long Min(long number1,long number2 ,long number3){
	long min = (number1 < number2 )?number1:number2 ;
	min = (min < number3)? min:number3; 
	return  min ;
}

你可能感兴趣的:(算法,剑指Offer,Java实现)