LeetCode703 流中第k大的元素
前言:
我们已经介绍了二叉搜索树的相关特性,以及如何在二叉搜索树中实现一些基本操作,比如搜索、插入和删除。熟悉了这些基本概念之后,相信你已经能够成功运用它们来解决二叉搜索树问题。
二叉搜索树的有优点是,即便在最坏的情况下,也允许你在O(h)
的时间复杂度内执行所有的搜索、插入、删除操作。
通常来说,如果你想有序地存储数据或者需要同时执行搜索、插入、删除等多步操作,二叉搜索树这个数据结构是一个很好的选择。
一个例子
问题描述:设计一个类,求一个数据流中第k大的数。
一个很显而易见的解法是,先将数组降序排列好,然后返回数组中第k个数。
但这个解法的缺点在于,为了在O(1)
时间内执行搜索操作,每次插入一个新值都需要重新排列元素的位置。从而使得插入操作的解法平均时间复杂度变为O(N)
。因此,算法总时间复杂度会变为O(N^2)
。
鉴于我们同时需要插入和搜索操作,为什么不考虑使用一个二叉搜索树结构存储数据呢?
我们知道,对于二叉搜索树的每个节点来说,它的左子树
上所有结点的值均小于
它的根结点的值,右子树
上所有结点的值均大于
它的根结点的值。
换言之,对于二叉搜索树的每个节点来说,若其左子树共有m
个节点,那么该节点是组成二叉搜索树的有序数组中第m + 1
个值。
你可以先独立思考这个问题。请先尝试把多个节点存储到树中。你可能还需要在每个节点中放置一个计数器,以计算以此节点为根的子树中有多少个节点。
设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。
你的 KthLargest
类需要一个同时接收整数 k
和整数数组nums
的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add
,返回当前数据流中第K大的元素。
示例:
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
说明:
你可以假设 nums
的长度≥ k-1
且k
≥ 1。
/* 算法思想: 这道题的数组是不断在变大的,所以每次第K大的数字都在不停的变化。那么我们其实只关心前K大个数字就可以了,所以我们可以使用一个最小堆来保存前K个数字,当再加入新数字后,最小堆会自动排序,然后把排序后的最小的那个数字去除,则堆中还是K个数字,返回的时候只需返回堆顶元素即可。 */ //算法实现: class KthLargest { public: KthLargest(int k, vector<int> nums) { for (int num : nums) { q.push(num); if (q.size() > k) q.pop(); } K = k; } int add(int val) { q.push(val); if (q.size() > K) q.pop(); return q.top(); } private: priority_queue<int, vector<int>, greater<int>> q; int K; }; /** * Your KthLargest object will be instantiated and called as such: * KthLargest obj = new KthLargest(k, nums); * int param_1 = obj.add(val); */