📜  给定范围内第 K 个最大的奇数(1)

📅  最后修改于: 2023-12-03 14:56:55.232000             🧑  作者: Mango

给定范围内第 K 个最大的奇数

问题描述

给定一个包含 $n$ 个正整数的数组 $arr$,以及正整数 $k$。请设计一个函数找出 $arr$ 中第 $k$ 个最大的奇数(如果不存在第 $k$ 个最大的奇数,则返回 $-1$)。

解法
方法一:排序

我们可以先将数组 $arr$ 中的所有奇数取出,并进行降序排序。然后直接返回排序后第 $k$ 个奇数即可。但是该方法时间复杂度为 $O(nlogn)$,不够高效。

代码实现:

def findKth(arr: List[int], k: int) -> int:
    odds = [x for x in arr if x % 2 == 1]
    odds.sort(reverse=True)
    return -1 if k > len(odds) else odds[k - 1]
方法二:堆

我们可以使用一个小根堆来维护 $arr$ 中所有奇数,当堆的大小小于等于 $k$ 时,我们把所有奇数依次加入堆中。当堆的大小超过 $k$ 时,我们就将堆顶元素(即最小的奇数)弹出,直到堆的大小等于 $k$。此时堆顶元素即为题目所求。

代码实现:

import heapq

def findKth(arr: List[int], k: int) -> int:
    odds = [-x for x in arr if x % 2 == 1]
    heapq.heapify(odds)
    while len(odds) > k:
        heapq.heappop(odds)
    return -1 if len(odds) < k else -odds[0]
方法三:快速选择算法

我们可以借助快速排序算法来解决。在快速排序的过程中,我们每次会选择一个数(例如pivot)将数组分割成两个部分,其中左半部分的数都小于等于pivot,右半部分的数都大于等于pivot。这个过程会产生一个整数 $m$,表示pivot所在的位置。

如果 $m$ 恰好等于 $k$,则pivot就是我们要找的答案。如果 $m$ 大于等于 $k$,说明第 $k$ 个最大的奇数在左半部分中,我们可以递归地处理左半部分;如果 $m$ 小于 $k$,说明第 $k$ 个最大的奇数在右半部分中,我们可以递归地处理右半部分。

代码实现:

import random

def quick_select(nums, k):
    def partition(left, right, pivot):
        nums[pivot], nums[right] = nums[right], nums[pivot]
        j = left - 1
        for i in range(left, right):
            if nums[i] >= nums[right]:
                j += 1
                nums[i], nums[j] = nums[j], nums[i]
        nums[right], nums[j + 1] = nums[j + 1], nums[right]
        return j + 1
    
    left, right = 0, len(nums) - 1
    while left <= right:
        pivot = random.randint(left, right)
        m = partition(left, right, pivot)
        if m == k:
            return nums[m]
        elif m > k:
            right = m - 1
        else:
            left = m + 1
    return -1

def findKth(arr: List[int], k: int) -> int:
    odds = [x for x in arr if x % 2 == 1]
    if len(odds) < k:
        return -1
    return quick_select(odds, k - 1)
总结

在本题中我们介绍了三种解法:排序、堆以及快速选择算法。其中排序算法最容易想到,但是时间复杂度较高;堆算法常数较小,但是仍然需要 $O(nlogk)$的时间复杂度;快速选择算法效率最高,具有 $O(n)$ 的时间复杂度。