快手 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)
    • 遍历其余 <math xmlns="http://www.w3.org/1998/Math/MathML"> n − k n - k </math>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 大
相关推荐
IT枫斗者18 小时前
前端部署后如何判断“页面是不是最新”?一套可落地的版本检测方案(适配 Vite/Vue/React/任意 SPA)
前端·javascript·vue.js·react.js·架构·bug
小码哥_常18 小时前
解锁AI编程密码:程序员常用的10个AI提示词
后端
wxy不爱写代码18 小时前
C++多线程
面试·职场和发展
直奔標竿19 小时前
Java开发者AI转型第二十七课!Spring AI 个人知识库实战(六)——全栈闭环收官,解锁前端流式渲染终极技巧
java·开发语言·前端·人工智能·后端·spring
金銀銅鐵19 小时前
[java] 编译之后的记录类(Record Classes)长什么样子(上)
java·jvm·后端
uzong21 小时前
我研读了 500 个 Spring Boot 生产级代码库,90% 都犯了这 7 个致命错误
后端
野生技术架构师21 小时前
金三银四面试总结篇,汇总 Java 面试突击班后的面试小册
java·面试·职场和发展
AI自动化工坊21 小时前
Late框架技术深度解析:5GB VRAM实现10倍AI编码效率的工程架构
人工智能·5g·架构·ai编程·late
空中海21 小时前
第六篇:架构篇 — 微服务、部署、高并发与专家级能力
微服务·云原生·架构