力扣热题 100:堆专题经典题解析
文章目录
- 一、数组中的第 K 个最大元素(题目 215)
- 1. 题目描述
- 2. 示例
- 3. 解题思路
- 4. 代码实现(Java)
- 5. 复杂度分析
- 二、前 K 个高频元素(题目 347)
- 1. 题目描述
- 2. 示例
- 3. 解题思路
- 4. 代码实现(Java)
- 5. 复杂度分析
- 三、数据流的中位数(题目 295)
- 1. 题目描述
- 2. 示例
- 3. 解题思路
- 4. 代码实现(Java)
- 5. 复杂度分析
在力扣(LeetCode)平台上,堆相关的题目是算法面试和练习中的重要部分。今天,我们就来详细解析堆专题中的几道经典题目,帮助大家更好地理解解题思路和技巧。
一、数组中的第 K 个最大元素(题目 215)
1. 题目描述
给定一个整数数组 nums
和一个整数 k
,找到数组中的第 k
个最大元素。
2. 示例
示例 1:
输入:nums = [3, 2, 1, 5, 6, 4], k = 2
输出:5
3. 解题思路
这道题主要考察堆的应用。我们可以使用一个大小为 k
的小顶堆来维护前 k
个最大元素。具体步骤如下:
- 构建一个大小为
k
的小顶堆。 - 遍历数组,将前
k
个元素放入堆中。 - 继续遍历数组,对于每个元素,如果它大于堆顶元素,则替换堆顶元素并调整堆。
- 最后,堆顶元素即为第
k
个最大元素。
4. 代码实现(Java)
import java.util.PriorityQueue;
public class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> heap = new PriorityQueue<>(k);
for (int i = 0; i < k; i++) {
heap.add(nums[i]);
}
for (int i = k; i < nums.length; i++) {
if (nums[i] > heap.peek()) {
heap.poll();
heap.add(nums[i]);
}
}
return heap.peek();
}
}
5. 复杂度分析
- 时间复杂度 :O(n log k),其中 n 是数组的长度。构建堆的时间复杂度为 O(k),后续每次调整堆的时间复杂度为 O(log k),总共需要调整 n - k 次。
- 空间复杂度 :O(k),需要使用一个大小为 k 的堆。
二、前 K 个高频元素(题目 347)
1. 题目描述
给定一个非空的整数数组 nums
,返回出现频率最高的前 k
个元素。
2. 示例
示例 1:
输入:nums = [1, 1, 1, 2, 2, 3], k = 2
输出:[1, 2]
3. 解题思路
这道题主要考察堆的应用。我们可以使用一个哈希表来统计每个元素的频率,然后使用一个大小为 k
的小顶堆来维护前 k
个高频元素。具体步骤如下:
- 使用哈希表统计每个元素的频率。
- 构建一个大小为
k
的小顶堆。 - 遍历哈希表,将元素按频率放入堆中。
- 如果堆的大小超过
k
,则弹出堆顶元素。 - 最后,堆中的元素即为前
k
个高频元素。
4. 代码实现(Java)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
public class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
Map<Integer, Integer> frequencyMap = new HashMap<>();
for (int num : nums) {
frequencyMap.put(num, frequencyMap.getOrDefault(num, 0) + 1);
}
PriorityQueue<Map.Entry<Integer, Integer>> heap = new PriorityQueue<>(
(a, b) -> a.getValue() - b.getValue()
);
for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
heap.add(entry);
if (heap.size() > k) {
heap.poll();
}
}
List<Integer> result = new ArrayList<>();
while (!heap.isEmpty()) {
result.add(heap.poll().getKey());
}
return result;
}
}
5. 复杂度分析
- 时间复杂度 :O(n log k),其中 n 是数组的长度。统计频率的时间复杂度为 O(n),后续每次调整堆的时间复杂度为 O(log k),总共需要调整 n 次。
- 空间复杂度 :O(n + k),需要使用哈希表存储频率,堆的大小为 k。
三、数据流的中位数(题目 295)
1. 题目描述
设计一个数据结构,能够高效地添加整数和找到当前所有元素的中位数。
2. 示例
示例 1:
输入:
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出:
[null, null, null, 1.5, null, 2.0]
3. 解题思路
这道题主要考察堆的应用。我们可以使用两个堆来维护数据流的中位数。具体步骤如下:
- 使用一个大顶堆
maxHeap
存储较小的一半元素,使用一个小顶堆minHeap
存储较大的一半元素。 - 添加元素时,根据元素大小决定放入哪个堆,并保持两个堆的大小平衡。
- 当两个堆的大小相等时,中位数是两个堆顶元素的平均值;当大小不等时,中位数是较大堆的堆顶元素。
4. 代码实现(Java)
import java.util.PriorityQueue;
public class MedianFinder {
private PriorityQueue<Integer> maxHeap;
private PriorityQueue<Integer> minHeap;
public MedianFinder() {
maxHeap = new PriorityQueue<>((a, b) -> b - a);
minHeap = new PriorityQueue<>();
}
public void addNum(int num) {
if (maxHeap.isEmpty() || num <= maxHeap.peek()) {
maxHeap.add(num);
} else {
minHeap.add(num);
}
if (maxHeap.size() > minHeap.size() + 1) {
minHeap.add(maxHeap.poll());
} else if (minHeap.size() > maxHeap.size()) {
maxHeap.add(minHeap.poll());
}
}
public double findMedian() {
if (maxHeap.size() == minHeap.size()) {
return (maxHeap.peek() + minHeap.peek()) / 2.0;
} else {
return maxHeap.peek();
}
}
}
5. 复杂度分析
- 时间复杂度 :每次添加元素的时间复杂度为 O(log n),其中 n 是当前元素的数量。查找中位数的时间复杂度为 O(1)。
- 空间复杂度 :O(n),需要使用两个堆存储所有元素。
以上就是力扣热题 100 中与堆相关的经典题目的详细解析,希望对大家有所帮助。在实际刷题过程中,建议大家多动手实践,理解解题思路的本质,这样才能更好地应对各种算法问题。