leetcode264- Ugly Number II- medium
Write a program to find the n
-th ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5
. For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12
is the sequence of the first 10
ugly numbers.
Note that 1
is typically treated as an ugly number, and n does not exceed 1690.
法1: heap。O(nLogn)
heap<Long> + set<Long>. 所有丑数来源都是曾经的丑数*2, *3, *5,那么你每次从heap里取出当前最小丑数后,先生成将来可能的丑数放进heap仓库。你就源源不断从仓库里按最小的一个个拿出来就好了。
细节:
1. 要用set去重,避免2*3 == 3*2的问题。
2.产生备选人比取到n要远远快得多,所以你虽然返回的是int,但你读到要返回的那个int前可能已经产生了很大的丑数,甚至是超过int范围的那种。那么越界产生错误的负数下一次就会被heap按最小的读出来,就错了,所以产生中间结果要用Long. 最后返回的时候转类型,而且没办法Long转int的,要用api, long.intValue();。
3.中间对heap set存的时候,针对2,3,5三个写的代码很像,请用for循环来写更优雅,而且可以很容易延伸到follow up: super ugly number.
法2: 指针法。O(n)
一根指针指着最新丑数,三根指针指着各自辅助235产生新丑数的旧丑数源。
pointer2定义是pointer2现在指的那个曾经的丑数,它要是*2,就能产生当前局面下有2因子的最小新丑数。point2从最小丑数开始慢慢移动产生*2新丑数,point3从最小丑数开始慢慢移动产生*3新丑数,point5同理。实际上每次真正最小的新丑数是这三种方式产生的备选人里最小的那一个。每一轮还要检查一下上一个新数是上一轮哪个质数因子贡献产生的,找到了你就可以向前挪动了(注意可能挪不只一根,就是2*3 == 3*2这种case。)
细节:移动指针的时候要每个指针都检查一次,可能要同时动两根的,比如对2*3 3*2都是最小的情况。要是写else if语句每次只动一根,那最后会产生很多重复的丑数。
实现1:
public class Solution { /** * @param n: a positive integer * @param primes: the given prime list * @return: the nth super ugly number */ public int nthSuperUglyNumber(int n, int[] primes) { // write your code here if (n <= 0) { return -1; } PriorityQueue<Long> minHeap = new PriorityQueue<Long>(); // P1: 小心!!要解决2*3 == 3*2这种去重问题! Set<Long> set = new HashSet<Long>(); int count = 1; // long number = 1; minHeap.offer((long) 1); while (count < n) { long number = minHeap.poll(); count++; // P2: 小心!要解决备选增长快很多,算出来的数超过int界限问题,变成负数下一轮就拿这个错误数了 // P3: 235三个部分长得很类似,用for处理更优雅。 for (int i = 0; i < primes.length; i++) { long newNumber = number * primes[i]; if (!set.contains(newNumber)) { minHeap.offer(newNumber); set.add(newNumber); } } } // P4: 不能简单地把long转成int,需要用api。 return minHeap.poll().intValue(); } }
实现2:
public class Solution { /** * @param n: An integer * @return: the nth prime number as description. */ public int nthUglyNumber(int n) { // write your code here if (n <= 0) { return -1; } int[] numbers = new int[n]; numbers[0] = 1; int newest = 0; int ptr2 = 0, ptr3 = 0, ptr5 = 0; for (int i = 0; i < n - 1; i++) { if (numbers[i] == 2 * numbers[ptr2]) { ptr2++; } if (numbers[i] == 3 * numbers[ptr3]) { ptr3++; } if (numbers[i] == 5 * numbers[ptr5]) { ptr5++; } numbers[i + 1] = Math.min(2 * numbers[ptr2], Math.min(3 * numbers[ptr3], 5 * numbers[ptr5])); } return numbers[n - 1]; } }