题目
给你一个整数 n
,请你找出并返回第 n
个 丑数 。
丑数 就是只包含质因数 2
、3
和/或 5
的正整数。
思路
返回第n个丑数,首先想到的就是最小堆。堆这种数据结构通常可以被看做一棵树的数组对象。它没有使用父指针或者子指针。堆根据“堆属性”来排序。题目中构建一个最小堆,第n次从堆中移出的数据就是排行第n的数据。
特性和作用
特性:
- 堆中某个结点的值总是不大于或不小于其父结点的值;
- 堆总是一棵完全二叉树。
作用:
- 快速找出一个集合中的最小值(或者最大值)-topk问题
- 构建优先队列
- 排序
实例: 假设有个这样的数据
10, 14, 25, 33, 81, 82, 99
一个从低到高有序排列的数组是以有效的最小堆,我们可以将这个堆画出来:
堆属性适用于每一个节点,因为父节点总是比它的字节点小。
注意: 并不是每一个最小堆都是一个有序数组!要将堆转换成有序数组,需要使用堆排序。
使用: 在java中通常会用优先队列(PriorityQueue)来构建二叉堆。PriorityQueue是继承AbstractQueue,AbstractQueue实现了Queue,继承了AbstractCollection。
PriorityQueue (Java Platform SE 8 )
-
Modifier and Type Method and Description boolean
add(E e)
将指定的元素插入到该优先级队列中。void
clear()
从这个优先级队列中移除所有的元素。Comparator<? super E>
comparator()
返回用于为该队列中的元素的比较,或null
如果这个队列是根据其元素的 natural ordering排序。boolean
contains(Object o)
返回true
如果此队列包含指定的元素。Iterator<E>
iterator()
返回此队列中元素的迭代器。boolean
offer(E e)
将指定的元素插入到该优先级队列中。E
peek()
检索,但不删除,这个队列头,或返回null
如果队列为空。E
poll()
检索并移除此队列的头,或返回null
如果队列为空。boolean
remove(Object o)
从该队列中移除指定元素的一个实例,如果它是存在的。int
size()
返回此集合中的元素的数目。Spliterator<E>
spliterator()
创建一个后期绑定和快速失败Spliterator
在队列中的元素。Object[]
toArray()
返回一个包含此队列中所有元素的数组。<T> T[]
toArray(T[] a)
返回包含此队列中的所有元素的数组;返回数组的运行时类型是指定的数组的运行时类型。
代码实现
public static int nthUglyNumber(int n) {
int[] factors = {2, 3, 5};
Set<Long> set = new HashSet<Long>();
PriorityQueue<Long> minHeap = new PriorityQueue<Long>();
set.add(1L);
minHeap.offer(1L);
int topK = 0;
for (int i = 0; i < n; i++) {
long curr = minHeap.poll();
topK = (int) curr;
for (int factor : factors) {
long next = curr * factor;
if (set.add(next)) {
minHeap.offer(next);
}
}
}
return topK;
}