Heap_1_application

  1. 堆: 支持动态维护最值的一种数据结构
  2. 347.前 K 个高频元素
  3. 703.数据流中的第K大的元素
  4. 295.数据流的中位数

堆: 支持动态维护最值的一种数据结构

1.可以用于解决数据流中的一些问题, 数据流这类问题存在一些动态性

347.前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1: 输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]

示例 2: 输入: nums = [1], k = 1 输出: [1]

分析:
1.可以用一个 hash 表记录下来每个出现的次数
2.用一个小顶堆 priority_queue> , 然后填充这个小顶堆
3.遍历 hash 表, 先保留下来前 k 个元素, 假设 map 元素个数小于 k, 那么前 k 个高频的就是已有的元素; 如果新遍历到的元素的出现次数大于堆顶这个出现次数, 淘汰掉当前小顶, 新的元素进入小顶堆; 实质上是永远淘汰最小的出现次数这种迭代思想, 剩下的就是频率最大的 K 个元素了
4.小顶堆的基础元素用 pair 进行迭代, first 和 second 分别保存的是 [值] 和 [出现的元素]

class Solution {
 public:
  vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> value2req;
    typedef pair<int, int> pii;
    for (auto& x: nums) {
      ++value2req[x];
    }
    auto cmp = [] (const pii a, const pii b) {
      return a.second > b.second;
    };
    // 用小顶堆保存 k 个元素, 按照元素出现次数排序
    priority_queue<pii, vector<pii>, decltype(cmp)> pq;
    for (auto& x: value2req) {
      if (pq.size() < k) {
        pq.push(make_pair(x.first, x.second));
      // 每次淘汰掉最小的出现次数, 并且保留 k 个元素
      } else if (pq.size() == k) {
        if (x.second > pq.top().second) {
          pq.pop();
          pq.push(make_pair(x.first, x.second));
        }
      }
    }
    vector<int> ans;
    while (!pq.empty()) {
      ans.push_back(pq.top().first);
      pq.pop();
    }
    return ans;
  }
};

703.数据流中的第K大的元素

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

输入:
[“KthLargest”, “add”, “add”, “add”, “add”, “add”]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]

解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8

分析:
1.因为是数据流, 我们时时刻刻都得维护前 k 个元素, 保持动态性, 所以我们用大顶堆去保存元素

class KthLargest {
 private:
  priority_queue<int, vector<int>, greater<int>> q;
  int k;

 public:
  KthLargest(int k, vector<int>& nums) {
    this->k = k;
    for (auto& x : nums) {
      add(x);
    }
  }

  int add(int val) {
    q.push(val);
    if (q.size() > k) {
      q.pop();
    }
    return q.top();
  }
};

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */

295.数据流的中位数

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
例如 arr = [2,3,4] 的中位数是 3 。
例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。
实现 MedianFinder 类:
MedianFinder() 初始化 MedianFinder 对象。
void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1: 输入 [“MedianFinder”, “addNum”, “addNum”, “findMedian”, “addNum”, “findMedian”]
[[], [1], [2], [], [3], []]
输出 [null, null, null, 1.5, null, 2.0]
解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3); // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

分析:
1.因为是数据流, 所以要保证添加和删除的动态性, 所以用到 堆 这种结构
2.我们用两个堆, 动态地维护大的一半元素和小的一半元素;大于等于中位数的, 称之为 largeHalf, 放入一个小顶堆; 小于中位数的, smallHalf, 放入大顶堆; 维持大集合元素数量大于等于小集合, 如果大集合元素数量更大, 那么直接返回大集合的堆顶; 否则是两个堆顶求和除以 2
3.需要持续保证 largeHalf 里面数量比 smallHalf 多或者相等

class MedianFinder {
 private:
  priority_queue<int, vector<int>, less<int>> largeHalf;
  priority_queue<int, vector<int>, greater<int>> smallHalf;
 public:
  MedianFinder() {
  }
  void addNum(int num) {
    // 每次先加入到 largeHalf
    largeHalf.push(num);
    // 迁移 largeHalf中的最小的过去 smallHalf
    smallHalf.push(largeHalf.top());
    largeHalf.pop();
    // 动态维护 largeHalf 数量多于或者等于 smallHalf
    if (largeHalf.size() < smallHalf.size()) {
      largeHalf.push(smallHalf.top());
      smallHalf.pop();
    }
  }

  double findMedian() {
    if (largeHalf.size() > smallHalf.size()) {
      return largeHalf.top();
    }
    return (largeHalf.top() + smallHalf.top()) / 2.0;
  }
};
/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

转载请注明来源, from goldandrabbit.github.io

💰

×

Help us with donation