You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Quick Select算法求数组第K大元素遇大K超时问题求助

解决LeetCode「数组中的第K个最大元素」Quick Select超时问题

我在做LeetCode的「数组中的第K个最大元素」题目时,采用Quick Select算法实现,但遇到超大数组且K值很大(如50000)时出现超时。尝试过随机打乱数组避免固定基准的最坏情况,但问题仍未解决——大数组搭配小K时正常,仅大K场景超时,想知道耗时原因及优化建议。

我的代码如下:

class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """

        n = len(nums)
        random.shuffle(nums)

        return self.quickselect(nums, 0, n-1, n-k)


    def quickselect(self, nums, l, r, k):

        piv = self.partition(nums, l, r)

        if k < piv:
            return self.quickselect(nums, l, piv-1, k)
        elif k > piv:
            return self.quickselect(nums, piv+1, r, k)
        else:
            return nums[piv]


    def partition(self, nums, l, r):
        
        piv = r
        i = l

        for j in range(l, r):

            if nums[j] <= nums[piv]:
                nums[i], nums[j] = nums[j], nums[i]
                i += 1

        nums[i], nums[piv] = nums[piv], nums[i]
        return i

问题分析与优化建议

1. 缺失random模块导入

代码中使用了random.shuffle但未导入random模块,在LeetCode环境中会触发NameError,虽不是超时直接原因,但会导致代码无法正常运行,需先添加import random。

2. 单路Partition在重复元素场景下的低效

当前的单路Partition将小于等于基准的元素移到左侧,当数组存在大量重复元素时,每次Partition仅能将基准元素放到正确位置,其余重复元素仍留在左右区间,导致递归/迭代次数剧增,时间复杂度退化为O(n²)——这是大K场景超时的核心原因。

3. 递归栈开销

递归实现的Quick Select在处理超大数组时,会产生额外的栈帧开销,尤其是K很大时(对应n-k很小,递归深度可达O(logn)),累积的栈开销会拖慢执行速度。

优化后的代码实现

改用三路快排Partition(将数组分为小于、等于、大于基准的三部分)+ 迭代实现,能有效处理重复元素并避免递归栈开销:

import random

class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        n = len(nums)
        random.shuffle(nums)
        target = n - k  # 转换为找第target小的元素(0-based)
        l, r = 0, n - 1
        
        while l <= r:
            lt, gt = self.partition_three_way(nums, l, r)
            if target < lt:
                r = lt - 1
            elif target >= gt:
                l = gt + 1
            else:
                # target落在等于基准的区间内,直接返回
                return nums[target]
        return -1

    def partition_three_way(self, nums, l, r):
        pivot = nums[r]
        lt = l  # 小于pivot的区域右边界(开区间)
        gt = r  # 大于pivot的区域左边界(开区间)
        i = l   # 当前遍历指针
        
        while i < gt:
            if nums[i] < pivot:
                nums[i], nums[lt] = nums[lt], nums[i]
                lt += 1
                i += 1
            elif nums[i] > pivot:
                gt -= 1
                nums[i], nums[gt] = nums[gt], nums[i]
            else:
                # 等于基准,直接跳过
                i += 1
        # 将基准元素移到等于区间的末尾
        nums[gt], nums[r] = nums[r], nums[gt]
        return lt, gt

额外优化思路

  • 基准选择优化:若想进一步降低最坏情况概率,可采用「中位数的中位数」策略选择基准,不过实现复杂度较高,一般三路Partition+随机打乱已能应对绝大多数场景。
  • 反向Partition适配大K:当K很大时(如K > n/2),可直接按降序进行Partition,目标索引设为k-1,避免转换为小索引的左半部分递归,进一步提升效率。

内容的提问来源于stack exchange,提问作者Justin Jin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 00:17:03