【BFPRT】数组中出现次数超过一半的数字

题目

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
如果不存在则输出0。

解答

1, 一个数在序列中出现次数超过一半,那这个数排序后一定也出现在中位数的位置上,这样的话就变成了求解一个序列的第K大的数,其中K=length/2;【BFPRT, Time: O(N), Space: log(N))】
2, 记录每个数出现的次数,最好情况遍历前n/2+1个即可,最差O(N), Space: O(N)
3, 要找的数字出现的次数比其他数字出现次数的和还多1,遍历,相同叠加,相异抵消,最后的次数还为1的数字即是。Time: O(N), Space: O(1)

三种方法都不错。还有一种无脑的排序,取中位数即可,排序用快排,时间复杂度N·log(N)
讲一下BFPRT的空间复杂度,select分治过程用到递归,需要栈空间存储,递归次数即是栈空间大小,由于每次只处理划分后的一半,因此最差情况需要递归log(N)次,空间复杂度为log(N)

本题还考察思维的全面性,除了实现功能以外,也应该对无效的输入进行响应的处理,因此有了核实操作。

代码实现:


# 方法1
# # BFPRT解法
# # Time: O(n), Space: O(n)
# class Solution:
#     def majorityElement(self, nums) -> int:
#         return self.BFPRT(nums, 0, len(nums) - 1, len(nums)//2)
# 
#     def BFPRT(self, nums, left, right, K):
#         """BFPRT算法"""
#         if left == right:
#             return nums[left]  # 不是排序,这要返回值
# 
#         base = self.medianOfMedians(nums, left, right)  # 找基准
#         base_index = self.partition(nums, left, right, nums.index(base))  # 基准归位
# 
#         if base_index == K:
#             return nums[base_index]
#         elif base_index > K:
#             return self.BFPRT(nums, left, base_index - 1, K)  # 递归左边。不是快排,这要返回值
#         else:
#             return self.BFPRT(nums, base_index + 1, right, K)  # 递归右边
# 
#     def medianOfMedians(self, nums, left, right):
#         """找中位数基准"""
#         length = right - left + 1
#         offset = 0 if length % 5 == 0 else 1  # 最后不够5个的算一组
#         groups = length // 5 + offset
# 
#         medians = []
#         for i in range(groups):
#             start = left + i * 5
#             end = start + 4
#             medians.append(self.get_median(nums[start: min(end, right) + 1]))
#         return self.BFPRT(medians, 0, groups - 1, groups // 2)  # 这里递归BFPRT,保证得到的是“准确中位数”(30% ~ 70%); 而非“近似中位数” 防止时间复杂度退化
# 
#     def get_median(self, nums):
#         """找5个数的中位数"""
#         return sorted(nums)[len(nums) // 2]  # 常数级别
# 
#     def partition(self, nums, left, right, base):
#         """常规partition"""
#         if left >= right:
#             return
#         temp = nums[base]
#         nums[base], nums[right] = nums[right], nums[base]
# 
#         max_index = left
#         for i in range(left, right):
#             if nums[i] <= temp:
#                 nums[i], nums[max_index] = nums[max_index], nums[i]
#                 max_index += 1
#         nums[max_index], nums[right] = nums[right], nums[max_index]
#         return max_index


# class Solution:
#     # 方法2
#     def MoreThanHalfNum_Solution(self, numbers):
#         if not numbers:
#             return []
#         if len(numbers) == 1:
#             return numbers[0]
#
#         d = {}
#         length = len(numbers)
#
#         for x in numbers:
#             if x not in d:
#                 d[x] = 1
#             else:
#                 d[x] += 1
#                 if d[x] == length//2+1:
#                     return x
#         return 0


class Solution:  
    # 方法3
    def MoreThanHalfNum_Solution(self, numbers):
        result = numbers[0]
        times = 1

        for x in numbers:
            if x == result:
                times += 1
            elif times == 0:
                result = x
                times = 1
            else:
                times -= 1
        # 核实
        length = len(numbers)
        cnt = 0
        for x in numbers:
            if x == result:
                cnt += 1
                if cnt == length // 2 + 1:
                    return result
        return 0


s = Solution()
ans = s.MoreThanHalfNum_Solution([1, 2, 3, 2, 2, 2, 5, 4, 2])
print(ans)



# 方法三
class Gift:
    def getValue(self, gifts, n):
        if not gifts:
            return 0
        if n == 1:
            return gifts[0]

        cnt = 0
        for x in gifts:
            if cnt == 0:
                cur = x
                cnt = 1
            else:
                if x == cur:
                    cnt += 1
                else:
                    cnt -= 1

        # 一定要校验
        cnt = 0
        for x in gifts:
            if x == cur:
                cnt += 1
                if cnt >= n//2:
                    return cur
        return 0

有序数组中出现次数超过25%的元素和方法三类似。

posted @ 2019-12-30 17:37  961897  阅读(178)  评论(0编辑  收藏  举报