题目 题解
题目描述:给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在众数。
哈希:
时间复杂度:O(n)
空间复杂度:O(n)
class Solution:
def majorityElement(self, nums: List[int]) -> int:
dic = {}
l=0 #长度,反正后面也要循环,多占用一个变量的空间省去len()的时间
for n in nums:
l+=1
if n in dic:
dic[n]+=1
else:
dic[n]=1
for key in dic.keys():
if dic[key]/l>=0.5:
return key
# 官方的哈希就是不一样
# 不熟的点:counter,带key的max()
def majorityElement(self, nums: List[int]) -> int:
counts = collections.Counter(nums)
return max(counts.keys(), key=counts.get)
cpp:
int majorityElement(vector<int>& nums) {
map<int,int> mp;
for(int i=0;i<nums.size();i++)
{
mp[nums[i]]++;
if(mp[nums[i]]>nums.size()/2)
return nums[i];
}
return -1;
}
排序:
时间复杂度:O(nlgn)
空间复杂度:O(1) 或者 O(n)
def majorityElement(self, nums):
nums.sort()
return nums[len(nums)//2]
随机化
随机选一个数,然后算它出现的次数是否大于一半。
时间复杂度:O(∞)
空间复杂度:O(n)
def majorityElement(self, nums):
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
分治
劝退过我的分治qwq
采用的经典分治法,分成好多个子问题(一直2分到只剩1项),子问题能够轻松得到结果,然后溯回,若区间大于1,判断左右众数相当与否,若不等需要将整个区间遍历一遍寻找真确的众数,若没有众数,返回右边的值,然后往上推,最终就能得到正确的众数。
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
def majority_element_rec(lo, hi):
# 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)
cpp:
class Solution {
public:
int majorityElement(vector<int>& nums) {
return majorityElement(nums, 0, nums.size() - 1);
}
int majorityElement(vector<int>& nums, int lptr, int rptr) {
if(lptr == rptr) return nums[lptr];
int mid = (lptr + rptr) / 2;
int left = majorityElement(nums, lptr, mid);
int right = majorityElement(nums, mid + 1, rptr);
if(left == right) return left;
int leftCount = 0;
int rightCount = 0;
for(int i = lptr; i < mid; i++) if(left == nums[i]) leftCount++;
for(int i = mid; i < rptr; i++) if(right == nums[i]) rightCount++;
return leftCount > rightCount ? left : right;
}
};
Boyer-Moore 投票算法
我们把众数记为 +1,把其他数记为 −1 ,将它们全部加起来,显然和大于 0 ,从结果本身我们可以看出众数比其他数多。
本质上, Boyer-Moore 算法就是找 nums 的一个后缀 suf ,其中 suf[0] 就是后缀中的众数。我们维护一个计数器,如果遇到一个我们目前的候选众数,就将计数器加一,否则减一。只要计数器等于 0 ,我们就将 nums 中之前访问的数字全部 忘记 ,并把下一个数字当做候选的众数。最后,总有一个后缀满足计数器是大于 0 的,此时这个后缀的众数就是整个数组的众数。
我们的候选者并不是真正的众数,但是我们在 遗忘 前面的数字的时候,要去掉相同数目的众数和非众数(如果遗忘更多的非众数,会导致计数器变成负数)。
Python:
def majorityElement(self, nums):
count = 0
candidate = None
for num in nums:
if count == 0:
candidate = num
count += (1 if num == candidate else -1)
return candidate
C++:
int majorityElement(vector<int>& nums) {
int count=0;
int candicate;
for(int item:nums)
{
if(count==0)
candicate = item;
item==candicate ? count++ : count--;
}
return candicate;
}
复杂度分析
时间复杂度:O(n)
Boyer-Moore 算法严格执行了 n 次循环,所以时间复杂度是线性时间的。
空间复杂度:O(1)
Boyer-Moore 只需要常数级别的额外空间。