📌  相关文章
📜  未排序数组中第K个最小最大元素|设置2(预期线性时间)(1)

📅  最后修改于: 2023-12-03 15:10:39.840000             🧑  作者: Mango

未排序数组中第K个最小/最大元素 | 设置2 (预期线性时间)

在现实生活中,我们经常需要在一个未排序的数组中搜索第k个最小或最大的元素。最简单的方法可能是对数组进行排序并返回第k个元素,但这种方法的时间复杂度为O(nlogn),并不够高效。该主题将介绍如何以O(n)的时间复杂度实现该目标。

解法

该解题主要是利用快速选择(QuickSelect)的思想。快速选择是快速排序的变体,它基于分治算法。该算法通过单次快排将数组的第k个元素移到正确的位置上。快速选择的时间复杂度为O(n),因为在任何一次递归中,我们都只需要对一个子数组进行操作。

在快速排序中,我们选择一个随机数作为基准元素,称为主元(Pivot)。我们将数组分为两部分,其中一部分所有元素都小于等于主元,而另一部分所有元素都大于主元。此时,主元的位置已经确定,我们可以将该位置与k进行比较:

  • 如果主元所在位置等于k,则说明找到了第k个元素,返回该元素即可;
  • 如果主元所在位置小于k,说明第k个元素在主元的右侧,这时我们递归处理主元右侧的子数组;
  • 如果主元所在位置大于k,说明第k个元素在主元的左侧,这时我们递归处理主元左侧的子数组。

最终,当我们找到第k个元素时,我们可以将其返回即可。

代码演示

下面是以Python语言实现的快速选择算法:

def partition(nums, left, right, pivot_index):
    pivot = nums[pivot_index]
    nums[right], nums[pivot_index] = nums[pivot_index], nums[right]
    idx = left
    for i in range(left, right):
        if nums[i] < pivot:
            nums[idx], nums[i] = nums[i], nums[idx]
            idx += 1
    nums[right], nums[idx] = nums[idx], nums[right]
    return idx

def quick_select(nums, left, right, target):
    if left == right:
        return nums[left]
    pivot_index = left + (right - left) // 2
    pivot_index = partition(nums, left, right, pivot_index)
    if target == pivot_index:
        return nums[target]
    elif target < pivot_index:
        return quick_select(nums, left, pivot_index - 1, target)
    else:
        return quick_select(nums, pivot_index + 1, right, target)

def kth_smallest(nums, k):
    return quick_select(nums, 0, len(nums) - 1, k - 1)

def kth_largest(nums, k):
    return quick_select(nums, 0, len(nums) - 1, len(nums) - k)

我们先介绍partition函数。该函数实现了快速排序中的分区(partition)操作,将数组中的元素分为两个部分。具体而言:

  • 首先,我们将主元移动到数组的最右端。
  • 然后,从左往右遍历数组,将所有小于主元的元素移到数组的左侧。
  • 最后,将主元移动到中间并返回其位置。

然后,我们介绍quick_select函数。该函数实现了快速选择的核心逻辑。该函数会在任意一个子数组中选择一个主元,并将数组分为两个部分。如果这个主元恰好就是我们找寻的元素,则函数返回该元素。否则,如果我们需要的元素在主元的左侧,则递归处理主元左侧的数组。如果需要的元素在主元的右侧,则递归处理主元右侧的数组。

最后,我们介绍kth_smallestkth_largest函数。这两个函数分别用于查找数组中第k个最小和最大的元素。它们都是调用quick_select函数,并将目标索引降低1来适应0-based的数组下标。

总结

在本文中,我们介绍了未排序数组中查找第k个最小或最大元素的算法。我们介绍了快速选择算法的原理,并给出了实现方式。与其它最优解一样,快速选择的时间复杂度为O(n)。