给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
首先可以想到查找算法,而查找就想到了哈希表, 记录每个元素出现的次数
def majorityElement(self, nums: List[int]) -> int:
# hash map, make use of the property that finding an elment in
# hash table takes O(1). But hash table uses O(n) of space
mydict = {}
max_num = len(nums) / 2
for num in nums:
if num in mydict.keys():
mydict[num] += 1
else:
mydict[num] = 1
for key, value in mydict.items():
if value > max_num:
return key
分治
将数组分成左右两部分,分别求出左半部分的众数 a1 以及右半部分的众数 a2,随后在 a1 和 a2 中选出正确的众数。我们使用经典的分治算法递归求解,直到所有的子问题都是长度为 1 的数组。长度为 1 的子数组中唯一的数显然是众数,直接返回即可。如果回溯后某区间的长度大于 1,我们必须将左右子区间的值合并。如果它们的众数相同,那么显然这一段区间的众数是它们相同的值。否则,我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数。
时间复杂度:O(nlogn)。分治函数会二分求解子问题,然后做两遍长度为n的线性扫描。
空间复杂度:O(logn)。尽管分治算法没有直接分配额外的数组空间,但在递归的过程中使用了额外的栈空间。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
def count(nums, left, right, obj):
count_num = 0
for i in range(left, right+1):
if nums[i] == obj:
count_num += 1
return count_num
def count_mod(nums, left, right):
if left == right:
return nums[left]
mid = (right + left) // 2
left_mod = count_mod(nums, left, mid)
right_mod = count_mod(nums, mid+1, right)
if left_mod == right_mod:
return left_mod
else:
if count(nums, left, right, left_mod) > ((right - left + 1) / 2):
return left_mod
if count(nums, left, right, right_mod) > ((right - left + 1) / 2):
return right_mod
return count_mod(nums, 0, len(nums)-1)
为了节省空间复杂度,出台了众数的专用算法摩尔投票
摩尔投票算法是基于这个事实:每次从序列里选择两个不相同的数字删除掉(或称为“抵消”),最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个。请首先认同这个事实
def majorityElement(self, nums: List[int]) -> int:
# morle voting, O(1) of space
candidate, count = nums[0], 1
for num in nums[1:]:
if num == candidate:
count += 1
else:
count -= 1
if count == 0:
candidate = num
count = 1
return candidate