169. 多数元素
题目:给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
链接:https://leetcode.cn/problems/majority-element
进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
个人思路
- 先统计每个数出现的次数,然后再返回符合条件的多数元素即可
class Solution:
def majorityElement(self, nums: List[int]) -> int:
temp = {}
for i in nums:
temp[i] = temp.get(i,0)+1
for i,j in zip(temp.keys(),temp.values()):
if j > len(nums) / 2:
return i
更简便的写法:
官方写法
class Solution:
def majorityElement(self, nums: List[int]) -> int:
counts = collections.Counter(nums)
return max(counts.keys(), key=counts.get)
复杂度分析
时间复杂度:O(n),其中 n 是数组 nums 的长度。
空间复杂度:O(n)。哈希表最多包含 n - ⌊ n/2 ⌋ 个键值对,所以占用的空间为 O(n)。
官方思路
- 排序
如果将数组 nums 中的所有元素按照单调递增或单调递减的顺序排序,那么下标为⌊ n/2 ⌋ 的元素(下标从 0 开始)一定是众数。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
nums.sort()
return nums[len(nums) // 2]
复杂度分析
时间复杂度:O(nlogn)。将数组排序的时间复杂度为O(nlogn)。
空间复杂度:O(logn)。如果使用语言自带的排序算法,需要使用O(logn) 的栈空间。如果自己编写堆排序,则只需要使用 O(1) 的额外空间。
- 随机化
因为超过⌊ n/2 ⌋ 的数组下标被众数占据了,这样我们随机挑选一个下标对应的元素并验证,有很大的概率能找到众数。
算法:
由于一个给定的下标对应的数字很有可能是众数,我们随机挑选一个下标,检查它是否是众数,如果是就返回,否则继续随机挑选。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
majority_count = len(nums) // 2
while True:
candidate = random.choice(nums)
if sum(1 for elem in nums if elem == candidate) > majority_count:
return candidate
- 分治
思路:
如果数 a 是数组 nums 的众数,如果我们将 nums 分成两部分,那么 a 必定是至少一部分的众数。
将数组分成左右两部分,分别求出左半部分的众数 a1 以及右半部分的众数 a2,随后在 a1 和 a2 中选出正确的众数。
算法:
我们使用经典的分治算法递归求解,直到所有的子问题都是长度为 1 的数组。长度为 1 的子数组中唯一的数显然是众数,直接返回即可。如果回溯后某区间的长度大于 1,我们必须将左右子区间的值合并。如果它们的众数相同,那么显然这一段区间的众数是它们相同的值。否则,我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
def majority_element_rec(lo, hi) -> int:
# base case; the only element in an array of size 1 is the majority
# element.
if lo == hi:
return nums[lo]
# recurse on left and right halves of this slice.
mid = (hi - lo) // 2 + lo
left = majority_element_rec(lo, mid)
right = majority_element_rec(mid + 1, hi)
# if the two halves agree on the majority element, return it.
if left == right:
return left
# otherwise, count each element and return the "winner".
left_count = sum(1 for i in range(lo, hi + 1) if nums[i] == left)
right_count = sum(1 for i in range(lo, hi + 1) if nums[i] == right)
return left if left_count > right_count else right
return majority_element_rec(0, len(nums) - 1)
- Boyer-Moore 投票算法
思路:
如果我们把众数记为 +1,把其他数记为 -1,将它们全部加起来,显然和大于 0,从结果本身我们可以看出众数比其他数多。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
count = 0
candidate = None
for num in nums:
if count == 0:
candidate = num
count += (1 if num == candidate else -1)
return candidate
时间复杂度:O(n)。Boyer-Moore 算法只对数组进行了一次遍历。
空间复杂度:O(1)。Boyer-Moore 算法只需要常数级别的额外空间
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/majority-element/solution/duo-shu-yuan-su-by-leetcode-solution/
来源:力扣(LeetCode)