快手 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 大
相关推荐
掘金码甲哥1 小时前
两张大图一次性讲清楚k8s调度器工作原理
后端
间彧1 小时前
Stream flatMap详解与应用实战
后端
Dream it possible!2 小时前
LeetCode 面试经典 150_链表_合并两个有序链表(58_21_C++_简单)
leetcode·链表·面试·1024程序员节
间彧2 小时前
Java Stream流两大实战陷阱:并行流Parallel误用、List转Map时重复键异常
后端
kkkkk0211063 小时前
黑马微服务保险(一)
笔记·微服务·架构
青鱼入云3 小时前
TraceId如何在Spring-Cloud微服务的REST调用中传递
微服务·架构·链路追踪
tan180°3 小时前
Linux网络UDP(10)
linux·网络·后端·udp·1024程序员节
正经教主4 小时前
【Trae+AI】和Trae学习搭建App_03:后端API开发原理与实践(已了解相关知识的可跳过)
后端·express
shepherd1264 小时前
破局延时任务(上):为什么选择Spring Boot + DelayQueue来自研分布式延时队列组件?
java·spring boot·后端·1024程序员节
开心-开心急了4 小时前
Flask入门教程——李辉 第5章: 数据库 关键知识梳理
笔记·后端·python·flask·1024程序员节