文章目录
- 题目--找出一堆随机数中的前 k 大数字
- PriorityQueue 类
- PriorityQueue 常用方法
- 题目--数组中的第 K 个最大元素
- 题目--二叉搜索树中第 K 小的元素
题目–找出一堆随机数中的前 k 大数字
找出一堆随机数中的前 k 大
数字(小根堆
),找出一堆随机数中的前 k 小
数字(大根堆
)。都一样
方法:快速排序。 最小堆法。
如果不了解堆这个数据结构:点击查看堆
最小堆法:
当数组规模很大时,排序的时间复杂度较高。为了优化,可以使用最小堆(Min-Heap)。在 Java 中可以利用 PriorityQueue
实现一个固定大小为 k 的最小堆来解决问题。
public static void main(String[] args) {
int[] nums = {57, 32, 56, 20, 54, 99, 47, 8, 35, 23, 30, 29, 63, 69, 58};
int k = 5; // 前k大
// 最小堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>(k);
// 遍历数组,维护最小堆
for (int num : nums) {
minHeap.offer(num);
if (minHeap.size() > k) {
minHeap.poll(); // 移除堆顶 即数组第一个元素
}
}
System.out.println("前 " + k + " 大的数: ");
while (!minHeap.isEmpty()) {
System.out.print(minHeap.poll() + " ");
}
}
PriorityQueue 类
PriorityQueue
是 Java 中实现堆(Heap)
数据结构的一个重要类,它基于优先级队列(Priority Queue)的概念。它使用堆来实现,默认是最小堆,即堆顶是队列中最小的元素。也可以通过传递自定义的比较器来实现最大堆。
PriorityQueue的底层实现是使用一个数组
来进行节点的存储
PriorityQueue 默认最小堆:
优先级队列表示为平衡二叉堆:queue[n] 的两个子节点为queue[2n+1] 和queue[2(n+1)]。优先级队列按比较器排序,如果比较器为空,则按元素的自然顺序排序:对于堆中的每个节点 n 和 n 的每个后代 d,n <= d。假设队列非空,则具有最小值的元素位于queue[0]中。【默认最小堆】
PriorityQueue 实现大根堆:
大根堆,则数组第一个元素最大
new PriorityQueue<>(Comparator.reverseOrder())
传入一个参数即可
public static void main(String[] args) {
// 自定义比较器,将 PriorityQueue 变为最大堆
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
maxHeap.offer(10);
maxHeap.offer(40);
maxHeap.offer(20);
maxHeap.offer(30);
System.out.println("最大堆中的元素:");
while (!maxHeap.isEmpty()) {
System.out.print(maxHeap.poll() + " "); // 输出时将从大到小
}
}
PriorityQueue 的特性:
-
自动排序:
PriorityQueue
会根据元素的优先级自动排序,默认排序方式是最小堆,意味着堆顶元素是队列中最小的元素。- 如果需要实现最大堆,可以传入一个自定义的比较器,使得元素按照降序排列。
-
不允许
null
元素:PriorityQueue
不允许插入null
元素,否则会抛出NullPointerException
。
-
线程不安全:
PriorityQueue
不是线程安全的。如果需要在多线程环境下使用,可以使用PriorityBlockingQueue
。
-
时间复杂度:
- 插入(
offer()
)和删除最小元素(poll()
)的时间复杂度均为O(log n)
,其中n
是队列的大小。 - 获取队列中最小元素的时间复杂度是
O(1)
,即通过peek()
方法。
- 插入(
PriorityQueue 常用方法
方法名 | 描述 |
---|---|
add(E e) | 插入元素 e 到队列中,若违反队列容量限制,抛出 IllegalStateException 。 |
offer(E e) | 插入元素 e 到队列中,返回 true 表示插入成功,若违反容量限制返回 false 。 |
poll() | 获取并移除队列的堆顶元素(即最小元素),若队列为空则返回 null 。 |
peek() | 获取但不移除队列的堆顶元素(即最小元素),若队列为空则返回 null 。 |
remove(Object o) | 从队列中移除指定的元素 o 。 |
isEmpty() | 检查队列是否为空。 |
size() | 返回队列的元素个数。 |
clear() | 清空队列中的所有元素。 |
contains(Object o) | 检查队列中是否包含指定的元素 o 。 |
题目–数组中的第 K 个最大元素
这个题也可以利用 PriorityQueue
秒了
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> queue = new PriorityQueue<>();
for (int num : nums) {
queue.offer(num);
if (queue.size() > k) {
queue.poll();
}
}
return queue.peek();
}
题目–二叉搜索树中第 K 小的元素
这个题也可以利用 PriorityQueue
秒了
你使用最大堆来找出第 k 小的元素。最大堆的特点是堆顶元素是当前堆中的最大值。
-
创建一个最大堆(PriorityQueue,指定了 Comparator.reverseOrder() 来使其成为最大堆)。
-
当堆的大小超过 k 时,移除堆顶元素(即当前最大元素)。这样,
堆中总是保持 k 个最小的元素
,堆顶始终是这 k 个元素中的最大值。
方法1:
public int kthSmallest(TreeNode root, int k) {
if (root == null) return 0;
ArrayList<Integer> list = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
list.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
// 二叉搜索树中第 K 小的元素
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
for (Integer i : list) {
maxHeap.offer(i);
if (maxHeap.size() > k) {
maxHeap.poll();
}
}
return maxHeap.peek();
}
方法2:利用二叉搜索树性质,中序遍历是有序的
public int kthSmallest(TreeNode root, int k) {
if (root == null) return 0;
// 利用中序遍历来获取第 k 小的元素
Stack<TreeNode> stack = new Stack<>();
TreeNode current = root;
int count = 0;
int res = root.val;
while (current != null || !stack.isEmpty()) {
// 先遍历左子树
while (current != null) {
stack.push(current);
current = current.left;
}
// 当前节点
current = stack.pop();
count++;
// 当第 k 小的元素出现时返回该元素
if (count == k) {
res = current.val;
break;
}
// 遍历右子树
current = current.right;
}
return res;
}
方法3:递归中序遍历
int res = 0;
int count = 0;
public int kthSmallest(TreeNode root, int k) {
if (root == null) return 0;
kthSmallest(root.left, k);
count += 1;
if (k == count) {
res = root.val;
}
kthSmallest(root.right, k);
return res;
}
❤觉得有用的可以留个关注~❤