导读:本期聚焦于小伙伴创作的《如何用C++实现快速选择算法查找第K大数且保证平均时间复杂度为O(n)》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何用C++实现快速选择算法查找第K大数且保证平均时间复杂度为O(n)》有用,将其分享出去将是对创作者最好的鼓励。

快速选择算法的核心思路是借鉴快速排序的分区操作,每次选择一个基准元素将数组分为两部分,根据基准元素的位置判断第K大数所在的区间,只递归处理目标区间,从而减少不必要的排序操作,实现平均O(n)的时间复杂度。该算法不需要对整个数组完成排序,仅需要定位到第K大元素的位置即可,在处理大规模数据查找第K大、第K小元素的场景中非常实用。

如何用C++实现快速选择算法查找第K大数且保证平均时间复杂度为O(n)

快速选择算法原理

快速选择算法的执行流程可以分为以下几个核心步骤:

  • 从数组中选择一个基准元素,通常可以选择数组的第一个元素、最后一个元素或者随机位置的元素
  • 对数组进行分区操作,将小于基准的元素放到基准左侧,大于基准的元素放到基准右侧,完成后基准元素会处于其最终排序位置
  • 假设数组长度为n,基准元素的最终位置为pos,那么基准元素就是数组的第(n-pos)大元素(如果按升序分区的话)
  • 如果(n-pos)等于K,说明当前基准元素就是目标第K大数,直接返回即可
  • 如果(n-pos)大于K,说明第K大数在基准元素的右侧区间,递归处理右侧区间
  • 如果(n-pos)小于K,说明第K大数在基准元素的左侧区间,递归处理左侧区间,同时更新K的值为K - (n-pos)

C++实现完整源码

以下是基于上述原理实现的C++查找第K大数的完整代码,包含分区函数和快速选择主函数,同时提供了测试示例:

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

// 分区函数,选择最右侧元素作为基准,返回基准的最终位置
int partition(std::vector<int>& nums, int left, int right) {
    int pivot = nums[right]; // 基准元素
    int i = left - 1; // 小于基准的元素的边界索引
    for (int j = left; j < right; j++) {
        if (nums[j] < pivot) {
            i++;
            std::swap(nums[i], nums[j]);
        }
    }
    // 将基准元素放到正确位置
    std::swap(nums[i + 1], nums[right]);
    return i + 1;
}

// 快速选择递归函数,查找第K大数
int quickSelect(std::vector<int>& nums, int left, int right, int k) {
    if (left == right) {
        return nums[left]; // 区间只有一个元素,直接返回
    }
    // 随机选择一个基准位置,避免最坏情况
    int randomIdx = left + rand() % (right - left + 1);
    std::swap(nums[randomIdx], nums[right]);
    
    int pos = partition(nums, left, right); // 基准的最终位置
    // 计算当前基准是第几大元素(升序分区下,位置越靠后元素越大)
    int bigCount = right - pos + 1;
    
    if (bigCount == k) {
        return nums[pos]; // 找到目标第K大数
    } else if (bigCount > k) {
        // 第K大数在基准右侧
        return quickSelect(nums, pos + 1, right, k);
    } else {
        // 第K大数在基准左侧,更新K值
        return quickSelect(nums, left, pos - 1, k - bigCount);
    }
}

// 对外暴露的查找第K大数接口
int findKthLargest(std::vector<int>& nums, int k) {
    srand(time(nullptr)); // 初始化随机种子
    return quickSelect(nums, 0, nums.size() - 1, k);
}

int main() {
    std::vector<int> testNums = {3, 2, 1, 5, 6, 4};
    int k = 2;
    int result = findKthLargest(testNums, k);
    std::cout << "数组中的第" << k << "大数是:" << result << std::endl;
    
    std::vector<int> testNums2 = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
    k = 5;
    result = findKthLargest(testNums2, k);
    std::cout << "数组中的第" << k << "大数是:" << result << std::endl;
    return 0;
}

代码说明与复杂度分析

关键细节说明

上述实现中加入了随机选择基准的逻辑,通过rand()函数随机选择一个位置的元素作为基准,避免数组本身有序时每次分区都只能减少一个元素,导致时间复杂度退化为O(n²)的最坏情况。分区函数采用常见的挖坑法变体,将小于基准的元素移动到左侧,最终将基准放到正确位置。

时间复杂度

快速选择算法的平均时间复杂度为O(n),因为每次递归处理的区间长度大致为上一轮的一半,总操作次数近似为n + n/2 + n/4 + ... ≈ 2n,即O(n)级别。最坏情况下时间复杂度为O(n²),但随机选择基准的方式可以让最坏情况出现的概率极低,实际使用中平均性能非常稳定。

空间复杂度

由于算法是递归实现,空间复杂度主要来自递归调用栈,平均情况下递归深度为O(logn),最坏情况下为O(n),如果改为迭代实现可以将空间复杂度优化到O(1)。

适用场景

快速选择算法适合需要在未排序数组中查找第K大、第K小元素的场景,尤其是数据规模较大、不需要完整排序结果的情况,相比先排序再取值的O(nlogn)方案性能优势明显。如果数组本身已经有序或者需要频繁查找不同K值的第K大数,建议先对数组进行一次排序,后续查找可以直接通过索引获取,效率更高。

快速选择算法C++第K大数O(n)_平均时间复杂度修改时间:2026-07-06 17:09:31

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。