【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)

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

代码实现:

# class Solution:
#     # 方法1
#     def get_median(self, nums):
#         '''计算5个数的中位数'''
#         begin, end = 0, len(nums) - 1
# 
#         sum = begin + end
#         mid = sum // 2 + sum % 2  # 这个地方加上sum%2是为了确保偶数个数时我们求的是中间两个数的后一个
#         return sorted(nums)[mid]  # 常量级别,影响不大,也可改用插入排序
# 
#     def BFPRT(self, nums, left, right):
#         '''选择partition基准: 划分每5个一组,求每组的中位数'''
#         num = right-left + 1
#         offset = 0 if num % 5 == 0 else 1  # 最后如果剩余的数不足5个,我们也将其分成一个小组,和前面同等对待
#         group = num//5 + offset
# 
#         median = []  # 中位数数组
#         for i in range(group):
#             begin = left + i * 5
#             end = begin + 4
#             Median = self.get_median(nums[begin:min(end, right) + 1])
#             median.append(Median)
#         return self.get_median(median)  # 这里是求中位数数组的中位数,再调用getmedian不就行了,为啥有的人还要用下面递归select,虽然也是正确的,但直接getmedian快一点啊
#         # return select(median, 0, groups-1, groups//2)  # 求出生成好的median数组的中位数,作为partation函数的划分值
# 
#     def partition(self, nums, left, right, base):
#         '''将基准base归位'''
#         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[max_index], nums[i] = nums[i], nums[max_index]
#                 max_index += 1
#         nums[max_index], nums[right] = nums[right], nums[max_index]
#         return max_index
# 
#     def select(self, nums, left, right, K):
#         '''快速选择过程'''
#         if left == right:
#             return nums[left]
# 
#         base = self.BFPRT(nums, left, right)                              # 选择基准
#         base_index = self.partition(nums, left, right, nums.index(base))  # 基准归位
# 
#         if base_index == K:   # 判断目前已归位的基准,是不是第K位
#             return nums[base_index]
#         elif base_index > K:  # 递归左半部分
#             return self.select(nums, left, base_index - 1, K)
#         else:                 # 递归右半部分
#             return self.select(nums, base_index + 1, right, K)
# 
#     def MoreThanHalfNum_Solution(self, numbers):
#         if not numbers:
#             return 0
# 
#         left, right = 0, len(numbers) - 1
#         length = len(numbers)
#         K = right // 2
#         ans = self.select(numbers, left, right, right - K + 1)  # 第K大,就是第N-K+1小
# 
#         # 核实
#         cnt = 0
#         for x in numbers:
#             if x == ans:
#                 cnt += 1
#                 if cnt == length // 2 + 1:
#                     return ans
#         return 0


# 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)

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

原文地址:https://www.cnblogs.com/ldy-miss/p/12120909.html