【Leetcode Top-K问题 BFPRT】第三大的数(414)

题目

给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)

示例 1:

输入: [3, 2, 1]
输出: 1
解释: 第三大的数是 1.

示例 2:

输入: [1, 2]
输出: 2
解释: 第三大的数不存在, 所以返回最大的数 2 .

示例 3:

输入: [2, 2, 3, 1]
输出: 1
解释: 注意,要求返回第三大的数,是指第三大且唯一出现的数。存在两个值为2的数,它们都排第二。

解答

思路:
1,题目要求时间复杂度必须是O(n),那么排序肯定是不行了,由于只是求第三大,那么可以先求出第一大,再求出第二大,再求第三大,总的时间复杂度O(N)。
2,BFPRT,专门解决TOP-K问题,但是因为序列中可能有重复值,所以在进行BFPRT之前,先去重,时间复杂度O(N)。(BFPRT应该是可以解决有重复值这个小问题的,But还不会............................> _ < )

通过代码如下:

方法一

class Solution:
    def thirdMax(self, nums: List[int]) -> int:
        l = list(set(nums))
        max_list = []
        sum = min(3, len(l))

        while sum:
            max = -float('inf')  # 负无穷
            for x in l:
                if x > max and x not in max_list:
                    max = x
            if max not in max_list:
                # l.remove(max)  remove时间复杂度为O(n)
                del l[l.index(max)]
                max_list.append(max)
            sum -= 1
        return max_list[-1] if len(max_list)>=3 else max_list[0]

BFPRT

class Solution:
    def thirdMax(self, nums) -> int:
        k = 3  # 代表求第三大
        nums = list(set(nums))
        if len(nums) < 3:
            return max(nums)

        def getmedian(lis):
            """返回序列lis中位数,在BFPRT中就是求每5个数小组的中位数"""
            begin = 0
            end = len(lis) - 1

            sum = begin + end
            mid = sum // 2 + sum % 2  # 这个地方加上sum%2是为了确保偶数个数时我们求的是中间两个数的后一个
            return sorted(lis)[mid]

        def BFPRT(nums, left, right):
            """分成每5个数一个小组,并求出每个小组内的中位数"""
            num = right - left + 1
            offset = 0 if num % 5 == 0 else 1  # 最后如果剩余的数不足5个,我们也将其分成一个小组,和前面同等对待
            groups = num // 5 + offset
            median = []  # 中位数数组
            for i in range(groups):
                begin = left + i * 5
                end = begin + 4
                Median = getmedian(nums[begin:min(end, right) + 1])
                median.append(Median)
            return select(median, 0, groups - 1, groups // 2)  # 求出生成好的median数组的中位数,作为partation函数的划分值

        def partition(nums, left, right, base):
            """在 nums[left, right] 将基准base归位"""
            temp = nums[base]
            nums[base], nums[right] = nums[right], nums[base]  # 基准和末尾元素互换

            max_index = left
            for i in range(left, right):  # 把所有小于基准的移到左边
                if nums[i] <= temp:  # 要等于啊!这里好坑的说.. 否则通不过[3, 3, 3, 3, 4, 3, 3, 3, 3]  k = 1
                    nums[max_index], nums[i] = nums[i], nums[max_index]
                    max_index += 1
            nums[right], nums[max_index] = nums[max_index], nums[right]  # 基准归位
            return max_index

        def select(nums, left, right, k_smallest):
            """在 nums[left, right] 找第k小的元素"""
            if left == right:  # 递归终止条件
                return nums[left]
            # pivot_index = random.randint(left, right)
            base = BFPRT(nums, left, right)
            base_index = partition(nums, left, right, nums.index(base))  # 选base为基准,并归位。
            if base_index == k_smallest:  # 判断目前已归位的基准,是不是第k_smallest位
                return nums[k_smallest]
            elif k_smallest < base_index:  # 递归左半部分
                return select(nums, left, base_index - 1, k_smallest)
            else:  # 递归右半部分
                return select(nums, base_index + 1, right, k_smallest)

        return select(nums, 0, len(nums) - 1, len(nums)-k)  # 第k大,是第n-k小

猜你喜欢

转载自www.cnblogs.com/ldy-miss/p/12035290.html