LeetCode 热题 100 堆

215. 数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 **k** 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

复制代码
输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

复制代码
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:

  • 1 <= k <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4

借助快速排序的思想解决topK问题,快速选择将数组划分为三块,对应三种情况进行查找,不断递归即可:

复制代码
[left, l - 1] [l, r] [r + 1, right]
  • 当k小于等于右区间的长度,既k <= right - r时,继续在右区间查找第k大的数。
  • 当k小于等于右区间+中间相同字符的长度,既k <= right - l + 1时,第k大的数就在中间区间为key
  • 当k大于右区间+中间相同字符的长度时,既k在左区间时,在左区间查找第k大的数减去右区间+中间相同字符的长度,既第k - (right - l + 1)大的数。
cpp 复制代码
int findKthLargest(vector<int>& nums, int k) {
    srand(time(nullptr));
    int left = 0, right = nums.size() - 1;
    return topK(nums, left, right, k);
}

int topK(vector<int>& nums, int left, int right, int k) {
    if (left == right)
        return nums[left];

    // 随机获取key
    int key = nums[rand() % (right - left + 1) + left];

    // 数组划分为三块
    int i = left, l = left, r = right;
    while (i <= r) {
        if (nums[i] < key)
            swap(nums[l++], nums[i++]);
        else if (nums[i] > key)
            swap(nums[r--], nums[i]);
        else
            i++;
    }

    // [left, l - 1] [l, r] [r + 1, right]
    if (k <= right - r)
        return topK(nums, r + 1, right, k);
    else if (k <= right - l + 1)
        return key;
    else
        return topK(nums, left, l - 1, k - (right - l + 1));
}

快速排序 的思想解决topK问题,快速选择将数组划分为三块,对应三种情况进行查找,不断递归即可:

复制代码
[left, l - 1] [l, r] [r + 1, right]
  • 当k小于等于右区间的长度,既k <= right - r时,继续在右区间查找第k大的数。
  • 当k小于等于右区间+中间相同字符的长度,既k <= right - l + 1时,第k大的数就在中间区间为key
  • 当k大于右区间+中间相同字符的长度时,既k在左区间时,在左区间查找第k大的数减去右区间+中间相同字符的长度,既第k - (right - l + 1)大的数。
cpp 复制代码
int findKthLargest(vector<int>& nums, int k) {
    srand(time(nullptr));
    int left = 0, right = nums.size() - 1;
    return topK(nums, left, right, k);
}

int topK(vector<int>& nums, int left, int right, int k) {
    if (left == right)
        return nums[left];

    // 随机获取key
    int key = nums[rand() % (right - left + 1) + left];

    // 数组划分为三块
    int i = left, l = left, r = right;
    while (i <= r) {
        if (nums[i] < key)
            swap(nums[l++], nums[i++]);
        else if (nums[i] > key)
            swap(nums[r--], nums[i]);
        else
            i++;
    }

    // [left, l - 1] [l, r] [r + 1, right]
    if (k <= right - r)
        return topK(nums, r + 1, right, k);
    else if (k <= right - l + 1)
        return key;
    else
        return topK(nums, left, l - 1, k - (right - l + 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 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

**进阶:**你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

优先级队列 + 哈希

cpp 复制代码
vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> hash;
    for (int i : nums) {
        hash[i]++;
    }

    auto cmp = [](pair<int, int>& a, pair<int, int>& b) {
        return a.second > b.second;
    };
    // 小根堆
    priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> que;
    for (auto& [num, count] : hash) {
        que.emplace(num, count);
        if (que.size() > k) {
            que.pop();
        }
    }
    vector<int> ans;
    while (!que.empty()) {
        ans.push_back(que.top().first);
        que.pop();
    }
    return ans;
}

快速排序

cpp 复制代码
void quickSort(vector<pair<int, int>>& v, int left, int right, int k) {
    if (left >= right) {
        return;
    }
    int key = v[rand() % (right - left + 1) + left].second;
    int i = left, l = left, r = right;
    while (i <= r) {
        if (v[i].second < key) {
            swap(v[i++], v[l++]);
        } else if (v[i].second > key) {
            swap(v[i], v[r--]);
        } else {
            i++;
        }
    }

    // [left, l - 1] [l, r] [r + 1, right]
    if (k <= right - r) {
        quickSort(v, r + 1, right, k);
    } else if (k <= right - l + 1) {
        return;
    } else {
        quickSort(v, left, l - 1, k - (right - l + 1));
    }
}

vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> hash;
    for (int i : nums) {
        hash[i]++;
    }

    vector<pair<int, int>> v;
    for (auto& p : hash) {
        v.push_back(p);
    }

    srand(time(nullptr));
    quickSort(v, 0, v.size() - 1, k);

    vector<int> ans;
    int i = v.size() -  1;
    while (k--) {
        ans.push_back(v[i--].first);
    }
    return ans;
}

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

提示:

  • -10^5 <= num <= 10^5
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 10^4 次调用 addNumfindMedian
  • que_min 是大根堆,用于存储数据流中较小的一半元素,堆顶元素是较小的一半元素中最大的元素。
  • que_max 是小根堆,用于存储数据流中较大的一半元素,堆顶元素是较大的一半元素中最小的元素。
cpp 复制代码
priority_queue<int, vector<int>, less<int>> que_min; // 大根堆
priority_queue<int, vector<int>, greater<int>> que_max; // 小根堆

MedianFinder() {}

void addNum(int num) {
    if (que_min.empty() || num <= que_min.top()) {
        que_min.push(num);
        if (que_min.size() > que_max.size() + 1) {
            que_max.push(que_min.top());
            que_min.pop();
        }
    } else {
        que_max.push(num);
        if (que_max.size() > que_min.size()) {
            que_min.push(que_max.top());
            que_max.pop();
        }
    }
}

double findMedian() {
    if (que_min.size() > que_max.size())
        return que_min.top();
    return (que_min.top() + que_max.top()) / 2.0;
}
相关推荐
uhakadotcom35 分钟前
PyTorch 分布式训练入门指南
算法·面试·github
uhakadotcom39 分钟前
PyTorch 与 Amazon SageMaker 配合使用:基础知识与实践
算法·面试·github
uhakadotcom1 小时前
在Google Cloud上使用PyTorch:如何在Vertex AI上训练和调优PyTorch模型
算法·面试·github
wen__xvn1 小时前
c++STL入门
开发语言·c++·算法
weisian1513 小时前
力扣经典算法篇-9-跳跃游戏(贪心算法,反向递推)
算法·leetcode·游戏
MCYH02064 小时前
C++抽卡模拟器
java·c++·算法·概率·原神
pystraf4 小时前
P10587 「ALFR Round 2」C 小 Y 的数 Solution
数据结构·c++·算法·线段树·洛谷
ゞ 正在缓冲99%…4 小时前
leetcode221.最大正方形
java·算法·动态规划
DataFunTalk4 小时前
大模型时代数据科学岗位的未来思考
前端·后端·算法
努力也学不会java4 小时前
【动态规划】深入动态规划 非连续子序列问题
java·数据结构·算法·leetcode·动态规划