快手 C++ 二面|如何获取前 K 个最大元素?

这个问题是数据结构与算法中的经典题目之一,常用于考察排序、堆、优先队列的应用。以下是几种常见的解法及其时间复杂度分析:

解法一:排序法(适合数据量不大)

思路:

  • 直接对数组进行排序
  • 取前 K 个元素
cpp 复制代码
#include <vector>
#include <algorithm>

std::vector<int> topKSort(std::vector<int>& nums, int k) {
    std::sort(nums.begin(), nums.end(), std::greater<int>());
    return std::vector<int>(nums.begin(), nums.begin() + k);
}

时间复杂度:

  • 排序时间复杂度:O(n log n)
  • 空间复杂度:O(1)

解法二:最小堆(推荐,适合大数据)

思路:

  • 使用大小为 K 的最小堆来保存当前最大的 K 个元素
  • 遍历整个数组,若当前元素比堆顶大,则替换堆顶
cpp 复制代码
#include <vector>
#include <queue>

std::vector<int> topKHeap(const std::vector<int>& nums, int k) {
    if (k == 0)
        return {};

    std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;

    for (int num : nums) {
        if (minHeap.size() < k) {
            minHeap.push(num);
        } else if (num > minHeap.top()) {
            minHeap.pop();
            minHeap.push(num);
        }
    }

    std::vector<int> result;
    while (!minHeap.empty()) {
        result.push_back(minHeap.top());
        minHeap.pop();
    }

    std::sort(result.rbegin(), result.rend()); // 可选:从大到小排序
    return result;
}

时间复杂度:

  • 时间复杂度:O(n log k)
    • 构建堆:O(k)
    • 遍历其余 n − k n - k n−k 个元素,每次 O(log k):总共 O((n-k) log k)
  • 空间复杂度:O(k)

🔥 解法三:快排的思想(Top-K 问题,适合不要求完整排序)

思路:

  • 类似快速排序中的分区(partition),选定一个"枢轴",将大于 pivot 的放左边,小于的放右边
  • 不断递归,直到找到第 K 个最大的数为止
cpp 复制代码
#include <vector>
#include <cstdlib> // for rand()

int partition(std::vector<int>& nums, int left, int right) {
    int pivot = nums[right], i = left;
    for (int j = left; j < right; ++j) {
        if (nums[j] >= pivot) {
            std::swap(nums[i], nums[j]);
            ++i;
        }
    }
    std::swap(nums[i], nums[right]);
    return i;
}

void quickSelect(std::vector<int>& nums, int left, int right, int k) {
    if (left >= right)
        return;
    int pivotIndex = partition(nums, left, right);
    if (pivotIndex == k)
        return;
    else if (pivotIndex < k)
        quickSelect(nums, pivotIndex + 1, right, k);
    else
        quickSelect(nums, left, pivotIndex - 1, k);
}

std::vector<int> topKQuickSelect(std::vector<int>& nums, int k) {
    quickSelect(nums, 0, nums.size() - 1, k);
    return std::vector<int>(nums.begin(), nums.begin() + k);
}

时间复杂度:

  • 平均:O(n)

    • 最坏(退化成链表):O(n^2)
  • 空间复杂度:O(1)(递归栈不计)

✅ 总结对比

方法 时间复杂度 空间复杂度 适用场景
排序法 O(n log n) O(1) 数据量小,代码简单
最小堆 O(n log k) O(k) 数据量大,k 远小于 n
快速选择 平均 O(n) O(1) 不关心顺序,只要前 K 大
相关推荐
tachibana217 分钟前
使用for range 的时候,它的地址会发生变化吗?
开发语言·后端·golang·go·语法
亥时无人机管理系统20 分钟前
无人机管理系统源码 | 私有化部署平台 航线规划飞行审批实时监控功能介绍
架构·私有化部署·无人机·低空经济·无人机巡检·无人机管理系统·一网统飞
蜗牛互联网23 分钟前
GPT-6 与 Intelligent UI:从回答到可审计操作的产品迁移
java·人工智能·后端
这个DBA有点耶1 小时前
时序数据库InfluxDB高基数问题深度拆解:内存索引膨胀机制与OOM完整链路
数据库·架构·dba
王中阳Go1 小时前
Go 后端转 AI:非科班怎么补?一张 3 列路线表(可保存)
开发语言·人工智能·后端·python·golang·大语言模型
IT_陈寒1 小时前
Vite热更新失效?你可能也踩了这个坑
前端·人工智能·后端
行百里er2 小时前
Redis 模块——JSON、向量搜索与时序
redis·后端
木子n12 小时前
软件架构设计实战-架构15.ISO 26262 架构设计要求:安全机制在架构里的落点
架构·autosar·功能安全·iso 26262
智码看视界2 小时前
C++智能指针全景剖析及面试20问
开发语言·c++·深度学习·面试·内存管理·智能指针
这个DBA有点耶3 小时前
为什么UPDATE比SELECT危险得多?锁范围等于扫描行数
数据库·架构·代码规范