快手 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 大
相关推荐
sufu10654 分钟前
SpringAI更新:废弃tools方法、正式支持DeepSeek!
人工智能·后端
嘵奇20 分钟前
Spring Boot拦截器详解:原理、实现与应用场景
java·spring boot·后端
八股文领域大手子21 分钟前
Java死锁排查:线上救火实战指南
java·开发语言·面试
XQ丶YTY1 小时前
大二java第一面小厂(挂)
java·开发语言·笔记·学习·面试
金刚猿1 小时前
openfeign 拦截器实现微服务上下文打通
微服务·云原生·架构
秋野酱2 小时前
基于javaweb的SpringBoot自习室预约系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端
Naylor2 小时前
微服务概述
微服务·架构·springcloud
weloveut2 小时前
西门子WinCC Unified PC的GraphQL使用手册
后端·python·graphql
面试官E先生3 小时前
【极兔快递Java社招】一面复盘|数据库+线程池+AQS+中间件面面俱到
java·面试
尽兴-4 小时前
Lambda架构与Kappa架构对比详解
hadoop·架构·kafka·lambda·kappa