【剑指 Offer 39. 数组中出现次数超过一半的数字】
题目描述:
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/
- 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
- 可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例:
- 示例1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
- 提示:
1 <= 数组长度 <= 50000
解析思路1:哈希表统计
-
可以用哈希表来快速统计每个元素出现的次数。使用哈希映射(HashMap)来存储每个元素以及出现的次数。对于哈希映射中的每个键值对,键表示一个元素,值表示该元素出现的次数。
代码(python3)
class Solution:
def majorityElement(self, nums: List[int]) -> int:
counts = collections.Counter(nums)
return max(counts.keys(), key=counts.get)
代码(cpp)
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int, int> count; // 创建哈希表
int majority = 0, res = 0; // 声明变量
for (int num: nums) { // 循环遍历数组 nums 并将数组中的每个元素加入哈希映射中
++count[num];
if (count[num] > res) { // 遍历哈希映射中的所有键值对
majority = num;
res = count[num];
}
}
return majority; // 返回值最大的键
}
};
- 或者
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map <int,int> map;
for(int n:nums)
if(++ map[n] > nums.size()/2)
return n;
return -1;
}
};
复杂度分析:
- 时间复杂度:O(n),其中 n 是数组 nums 的长度。我们遍历数组 nums 一次,对于 nums 中的每一个元素,将其插入哈希表都只需要常数时间。
- 空间复杂度:O(n)。
解析思路2:排序
-
若将数组 nums 中的所有元素按照单调递增或单调递减的顺序排序,那么下标为 ⌊ n/2⌋ 的元素(下标从 0 开始)一定是众数。
-
算法流程示意图:
-
奇数与偶数的排序情况:
- 不管对于哪种情况,数组下面的线表示如果众数是数组中的最小值时覆盖的下标,数组下面的线表示如果众数是数组中的最大值时覆盖的下标。对于其他的情况,这条线会在这两种极端情况的中间。对于这两种极端情况,它们会在下标为(n/2)的地方有重叠。因此,无论众数是多少,返回下标(n/2)对应的值都是正确的。
代码(python3)
class Solution:
def majorityElement(self, nums: List[int]) -> int:
nums.sort()
res = nums[len(nums) // 2]
return res
#return nums[len(nums) // 2
代码(cpp)
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
};
复杂度分析:
- 时间复杂度:O(nlogn)。将数组排序的时间复杂度为 O(nlogn)。
- 空间复杂度:O(logn)。如果使用语言自带的排序算法,需要使用 O(logn) 的栈空间。如果自己编写堆排序,则只需要使用 O(1) 的额外空间。
解析思路3: Moore投票
-
摩尔投票法,投目标值++,不投–,核心理念为 票数正负抵消,超过一半以上的人投,出结果啦
-
算法流程示意图:
-
算法流程:
代码(python3)
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
- 或者
class Solution:
def majorityElement(self, nums: List[int]) -> int:
votes = 0
for num in nums:
if votes == 0: x = num
votes += 1 if num == x else -1
return x
代码(cpp)
class Solution {
public:
int majorityElement(vector<int>& nums) {
//摩尔投票法,投目标值++,不投--,超过一半以上的人投,出结果啦
int count = 0, candidate = 0;
for(int n : nums)
{
if(count == 0) candidate = n;
if(n == candidate) count++;
else count--;
}
return candidate;
}
};
- 或者
class Solution {
public:
int majorityElement(vector<int>& nums) {
int x = 0, votes = 0;
for(int num : nums){
if(votes == 0) x = num;
votes += num == x ? 1 : -1;
}
return x;
}
};
-
复杂度分析:
-
时间复杂度:O(n)。Moore 算法只对数组进行了一次遍历。
-
空间复杂度:O(1)。Moore 算法只需要常数级别的额外空间。