leetcode 703. Kth Largest Element in a Stream & c++ priority_queue & minHeap/maxHeap

本文介绍如何使用C++ STL中的小顶堆(priority_queue)来实现在数据流中查找第K大元素的功能。通过设计一个类KthLargest,利用小顶堆保存最大的K个数字,当新元素加入时,调整堆以保持根节点始终为第K大元素。文章提供了一个示例,展示如何初始化类并调用add方法返回当前流中的第K大元素。

703. Kth Largest Element in a Stream & c++ priority_queue & minHeap/maxHeap

相关链接

leetcode
c++ priority_queue cplusplus
c++ priority_queue cnblog

背景知识

  堆是算法中常用的数据结构之一,其结构是完全二叉树,但实现的方法最常见的是使用数组;这里主要介绍小顶堆,其根元素最小,对于任何一个节点来说,他都比其后代要小;访问器根元素的时间为O(1);树的高度严格控制在 log(n) 以内,故每次插入元素的时间为 O(log(n)).

  在c++的STL中提供了现成的堆模板给我们使用;你需要引入头文件queue.h即可;
具体使用如下

#include<queue.h>

using namespace std;

int main()
{
    //大顶堆
    priority_queue<int> maxHeap;
    //等价于
    priority_queue<int, vector<int>, less<int> > maxHeap1;

    //小顶堆
    priority_queue<int, vector<int>, greater<int> > maxHeap1;

}

描述

   Design a class to find the kth largest element in a stream. Note that it is the kth largest element in the sorted order, not the kth distinct element.

  Your KthLargest class will have a constructor which accepts an integer k and an integer array nums, which contains initial elements from the stream. For each call to the method KthLargest.add, return the element representing the kth largest element in the stream.

Example:

int k = 3;
int[] arr = [4,5,8,2];
KthLargest kthLargest = new KthLargest(3, arr);
kthLargest.add(3); // returns 4
kthLargest.add(5); // returns 5
kthLargest.add(10); // returns 5
kthLargest.add(9); // returns 8
kthLargest.add(4); // returns 8
Note:
You may assume that nums’ length ≥ k-1 and k ≥ 1.

solution1

  用一个小顶堆保存最大的K个数字;根节点处为K个数字中最小的一个,也是所有数据中第k大的数字;每当有新元素进来的时候,拿走小顶堆中最小的元素;


using namespace std;

class KthLargest {
public:
	int k;
	priority_queue<int, vector<int>, greater<int> > minHeap;
public:
	KthLargest(int k, vector<int>& nums) :minHeap(nums.begin(), nums.end()) {
		this->k = k;
	}

	int add(int val) {
		minHeap.push(val);
		while (k < minHeap.size())
			minHeap.pop();
		return minHeap.top();
	}
};



int main()
{
	priority_queue<int> maxHeap;
	//priority_queue<int, vector<int>, less<int> > maxHeap;
	priority_queue<int, vector<int>, greater<int> > minHeap;

	int k = 3;
	vector<int> arr = { 4,5,8,2 };
	
	KthLargest kthLargest =KthLargest(3, arr);
	cout<<kthLargest.add(3);   // returns 4
	cout << kthLargest.add(5);   // returns 5
	cout << kthLargest.add(10);  // returns 5
	cout << kthLargest.add(9);   // returns 8
	cout << kthLargest.add(4);   // returns 8

	system("pause");
}
当然可以!下面是对你的代码进行 **简化、优化逻辑、增强可读性**,并给出 **准确的时间复杂度分析**。 --- ### ✅ 优化目标 1. **简化冗余代码** 2. **强化堆平衡逻辑的清晰度** 3. **修复中位数返回类型问题(应为 `double`)** 4. **提升算法鲁棒性和规范性** 5. **分析时间与空间复杂度** --- ### ✅ 简化并优化后的完整代码 ```cpp #include &lt;iostream&gt; #include &lt;queue&gt; #include &lt;vector&gt; using namespace std; class MedianFinder { private: priority_queue&lt;int&gt; maxHeap; // 左边:大顶堆(较小的一半) priority_queue&lt;int, vector&lt;int&gt;, greater&lt;int&gt;&gt; minHeap; // 右边:小顶堆(较大的一半) public: // 添加数字并保持平衡 void addNum(int num) { if (maxHeap.empty() || num &lt;= maxHeap.top()) { maxHeap.push(num); } else { minHeap.push(num); } // 调整大小,确保:maxHeap.size() == minHeap.size() 或 多 1 if (maxHeap.size() &gt; minHeap.size() + 1) { minHeap.push(maxHeap.top()); maxHeap.pop(); } else if (minHeap.size() &gt; maxHeap.size()) { maxHeap.push(minHeap.top()); minHeap.pop(); } } // 返回中位数(注意:可能是浮点) double findMedian() { if (maxHeap.size() == minHeap.size()) { return (static_cast&lt;double&gt;(maxHeap.top()) + minHeap.top()) / 2.0; } else { return maxHeap.top(); // maxHeap 多一个 } } }; int main() { MedianFinder mf; int n, num; cout &lt;&lt; &quot;请输入整数个数: &quot;; cin &gt;&gt; n; cout &lt;&lt; &quot;请输入&quot; &lt;&lt; n &lt;&lt; &quot;个整数: &quot;; for (int i = 0; i &lt; n; ++i) { cin &gt;&gt; num; mf.addNum(num); } cout &lt;&lt; &quot;中位数为: &quot; &lt;&lt; mf.findMedian() &lt;&lt; endl; return 0; } ``` --- ### 🔍 主要改进说明: | 改进项 | 说明 | |--------|------| | ✅ `findMedian()` 返回 `double` | 原始代码用 `int` 会截断小数,如 `(1+2)/2=1` 错误。现在使用 `double` 并显式转换,避免精度丢失。 | | ✅ 移除无用构造函数 | `MedianFinder(){}` 是默认的,无需显式定义。 | | ✅ 优化堆平衡逻辑 | 条件判断更清晰:&lt;br&gt; - 左堆最多比右堆多 1&lt;br&gt; - 右堆不能比左堆大(否则左堆补上) | | ✅ 使用 `++i` 替代 `i++` | 微小性能优化(虽编译器会优化,但习惯更好) | | ✅ 清晰注释和格式 | 提高可读性 | --- ### 📊 时间复杂度分析 | 操作 | 时间复杂度 | 原因 | |------|------------|------| | `addNum(int num)` | **O(log n)** | 插入到堆中需要调整结构,堆的高度是 `log n` | | `findMedian()` | **O(1)** | 只访问堆顶元素(最大/最小),不涉及遍历或删除 | &gt; 💡 总体处理 $ N $ 个元素的总时间为: &gt; $$ &gt; O(N \log N) &gt; $$ 因为每个插入是 $ O(\log N) $,共插入 $ N $ 次。 --- ### 🧱 空间复杂度 - 所有元素都存储在两个堆中,总共 $ N $ 个元素。 - **空间复杂度:`O(N)`** --- ### ✅ 示例运行 输入: ``` 请输入整数个数: 6 请输入6个整数: 3 1 4 1 5 9 ``` 输出(可能): ``` 中位数为: 3 ``` &gt; 解释:排序后 `[1,1,3,4,5,9]` &rarr; 中位数 = (3+4)/2 = 3.5?等等! ⚠️ 注意:我们上面代码返回的是 `double`,所以实际输出是 `3.5`! 你之前返回 `int` 会导致错误结果(比如 `3`),现在已修正。 ✅ 正确中位数:`(3 + 4) / 2 = 3.5` --- ### ✅ 补充建议 如果你想支持动态查询任意时刻的中位数(流式数据),这个结构非常合适,常用于: - 数据流中找中位数(LeetCode 295) - 实时统计系统 - 滑动窗口中位数预处理等 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值