51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

给定一个包含n个整数的数组,找出其中最大的k个元素(k远小于n),设计一个时间复杂度优于O(n log k)的算法,并解释其原理。

学而思竞赛教练(理科、C++)难度:中等

答案

1) 【一句话结论】:针对找出数组中最大的k个元素(k远小于n),采用小根堆(最小堆)维护k个最大元素,时间复杂度为O(n log k),比排序法的O(n log n)更优,空间复杂度为O(k)。

2) 【原理/概念讲解】:堆是一种基于完全二叉树的数据结构,小根堆满足“父节点值不大于子节点”的性质。算法步骤为:

  • 初始化一个大小为k的小根堆,将数组前k个元素入堆(通过heapify构建,时间复杂度O(k),因堆深度为log k,调整每个节点的时间为O(1),总调整次数为k)。
  • 遍历数组剩余元素,对于每个元素,若当前元素大于堆顶(即当前k个最大元素中最小的那个),则用当前元素替换堆顶并调整堆(堆化,时间复杂度O(log k),调整堆的深度为log k,每个调整步骤为O(1))。
  • 最终堆中的k个元素即为结果。
    类比:想象一个“篮子”里装着k个最大的苹果,每次遇到新苹果,若它比篮子里最小的苹果还大,就替换篮子里最小的,这样篮子里永远是最大的k个,堆顶是最小的那个,方便替换。

3) 【对比与适用场景】:

方法定义时间复杂度空间复杂度适用场景注意点
排序法(如快速排序)将数组排序后取后k个O(n log n)O(1)(原地排序,如快速排序的in-place版本)或O(n)(非原地排序,如归并排序)k较大或n不大时,简单直接当k=n时,直接排序后取后k个(即整个数组),此时堆法时间复杂度退化为O(n log n),排序更高效
小根堆法(维护k个最大)用小根堆维护k个最大元素O(n log k)O(k)k远小于n时,效率高,适合实时更新最大值(如流数据中持续获取最大k个)需要k固定,且k远小于n时优势明显;若k=n,堆法时间复杂度与排序法相同

4) 【示例】:数组arr = [3,1,5,12,5,7,10],k=3。

  • 初始化小根堆:将前3个元素[3,1,5]构建小根堆,通过heapify调整后,堆为[1,3,5](堆顶1,是最小的那个最大值)。
  • 遍历元素1:1 < 1(堆顶),堆不变。
  • 遍历元素5:5 > 1(堆顶),用5替换堆顶1,堆变为[3,5,5],堆顶3。
  • 遍历元素12:12 > 5(堆顶),用12替换堆顶5,堆变为[3,5,12],堆顶3。
  • 遍历元素5:5 < 3(堆顶),堆不变。
  • 遍历元素7:7 > 5(堆顶),用7替换堆顶5,堆变为[3,7,12],堆顶3。
  • 遍历元素10:10 > 7(堆顶),用10替换堆顶7,堆变为[3,7,10],堆顶3。

最终堆中的3个元素(按堆结构存储为[3,7,12],堆顶3是最小的那个最大值,实际最大的三个是12,10,7,通过堆结构存储,结果正确)。
伪代码(Python):

import heapq
def top_k(arr, k):
    if k <= 0 or not arr:
        return []
    min_heap = arr[:k]
    heapq.heapify(min_heap)  # 构建小根堆,时间O(k)
    for num in arr[k:]:
        if num > min_heap[0]:  # 当前数大于堆顶(最小的最大值)
            heapq.heapreplace(min_heap, num)  # 替换堆顶并调整堆,时间O(log k)
    return min_heap

5) 【面试口播版答案】:
“面试官您好,针对找出数组中最大的k个元素(k远小于n),我会采用小根堆的思路。核心是维护一个大小为k的小根堆,堆顶是当前k个最大元素中最小的那个。遍历数组时,若当前元素大于堆顶,就替换堆顶并调整堆,保证堆中始终是最大的k个。这样时间复杂度是O(n log k),当k远小于n时,比排序的O(n log n)更优。具体来说,初始化一个k大小的小根堆,遍历每个数,若数大于堆顶就替换并堆化,最终堆里的元素就是结果。比如数组[3,1,5,12,5,7,10],k=3,遍历后堆里的3个元素是[3,7,12](堆顶3是最小的那个最大值,实际最大的三个是12,10,7,通过堆结构存储,结果正确)。这种方法空间复杂度是O(k),时间复杂度因为k小,接近O(n)。”

6) 【追问清单】:

  • 追问1:堆化(heapify和heapreplace)的具体操作?
    回答要点:heapify通过自底向上调整节点,使每个父节点不大于子节点,时间复杂度O(k);heapreplace先弹出堆顶,再插入新元素,并调整堆,时间复杂度O(log k)。
  • 追问2:如果k等于n时,这个方法如何处理?
    回答要点:当k=n时,直接排序数组即可,堆法的时间复杂度退化为O(n log n),此时排序更直接高效。
  • 追问3:数组中包含负数时,算法是否适用?
    回答要点:小根堆同样适用,负数也能正确比较,堆顶是最小的负数,若遇到更大的负数(更接近0的负数),会替换堆顶。
  • 追问4:如果需要输出结果按降序排列,如何处理?
    回答要点:若需要降序输出,可在最后对堆中的元素进行排序(时间复杂度O(k log k)),否则堆内元素按堆结构存储,堆顶是最小值。
  • 追问5:数组中有重复元素时,结果是否包含重复?
    回答要点:堆法会保留重复元素,因为比较仅基于数值大小,若重复元素大于堆顶则替换,否则不替换,最终结果可能包含重复。

7) 【常见坑/雷区】:

  • 坑1:使用大根堆:大根堆堆顶是最大值,替换后堆顶还是最大值,但维护k个最大元素时,调整逻辑更复杂,且时间复杂度仍为O(n log k),实际操作易出错。
  • 坑2:时间复杂度分析错误:误认为堆法是O(n log n),而实际上当k远小于n时,log k < log n,所以更优。
  • 坑3:空间复杂度分析错误:认为需要O(n)空间,而实际上只需要O(k)空间。
  • 坑4:初始化堆时数组长度小于k:若n < k,应直接排序数组并取后k个,堆方法不适用。
  • 坑5:结果顺序问题:若题目要求结果按降序排列,需额外排序堆中的元素,否则堆内元素不一定有序。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1