题目:多数元素
链接:link
说明
这样的数有且只有一个,如果数组中有两个或更多的元素出现次数都超过数组长度的一半,那么这些元素的出现次数总和就会超过数组长度,这是不可能的
解题方法
方法一:哈希算法
思路:
- 哈希表可以用来统计每个元素出现的次数,然后找到出现次数超过数组长度一半的元素。
- 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
代码
public static int majorityElement(int[] nums) {
Map<Integer, Integer> countMap = new HashMap<>();
for (int num : nums) {
countMap.put(num, countMap.getOrDefault(num, 0) + 1);
if (countMap.get(num) > nums.length / 2) {
//如果有则立即返回
return num;
}
}
// 如果没有多数元素,抛出异常或返回默认值
throw new IllegalStateException("No majority element found");
}
方法二:排序
思路
- 如果数组是有序的,那么多数元素一定位于数组的中间位置。可以先对数组进行排序,然后返回中间位置的元素。
- 时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度 O ( l o g n ) O(logn) O(logn)
代码
public static int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
方法三:摩尔投票法
思路
- 由于多数元素的出现次数超过一半,那么在遍历数组的过程中,我们总是可以保留一个候选的多数元素,并统计它的出现次数。当遇到一个与当前候选多数元素不同的元素时,就将它的投票次数减1,如果相同,则加1;如果出现次数减到0,就更换候选元素。由于多数元素的出现次数超过一半,最终留下的候选元素一定是多数元素。
- 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
代码
public int majorityElement(int[] nums) {
int count = 0;
int candidate = 0;
for(int num : nums) {
if(count == 0) {
candidate = num;
}
count += (candidate == num) ? 1 : -1;
}
return candidate;
}
拓展:229. 多数元素 II
链接:link
思路
- 初始化两个候选变量 candidate1 和 candidate2,以及两个计数器 count1 和 count2,都设为 0。遍历数组中的每个元素:
如果当前元素等于 candidate1,则 count1 加 1。如果当前元素等于 candidate2,则 count2 加 1。
如果当前元素既不等于 candidate1 也不等于 candidate2,如果 count1 为 0,则将当前元素设为 candidate1,并将 count1 设为 1。否则,如果 count2 为 0,则将当前元素设为 candidate2,并将 count2 设为 1。
如果 count1 和 count2 都不为 0,则两个计数器都减 1。 - 遍历完数组后,candidate1 和 candidate2 可能不是真正的答案,因为它们的计数器可能已经被减到了 0。因此,我们需要再次遍历数组,统计 candidate1 和 candidate2 的真实出现次数,看它们是否超过 ⌊ n/3 ⌋。
- 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
- 总结:如果至多选一个代表,那他的票数至少要超过一半(⌊ 1/2 ⌋)的票数;如果至多选两个代表,那他们的票数至少要超过 ⌊ 1/3 ⌋ 的票数;如果至多选m个代表,那他们的票数至少要超过 ⌊ 1/(m+1) ⌋ 的票数。
class Solution {
public List<Integer> majorityElement(int[] nums) {
//结果
List<Integer> result = new ArrayList<>();
//初始化两个候选人和各自的投票数
int can1 = nums[0], count1 = 0;
int can2 = nums[0], count2 = 0;
//配对,抵消处理阶段
for (int num : nums) {
//投票
if(can1 == num) {
count1++;
continue; //当前候选人获得投票,后面都不需要执行
}
if(can2 == num) {
count2++;
continue; //同上
}
//当前面两个候选人都未获得投票时,进行配对替换,看哪个候选人将要被替代
if (count1 == 0) {
can1 = num;
count1++;
continue;
}
if (count2 == 0) {
can2 = num;
count2++;
continue;
}
//如果都未被替代,则各自抵消票数
count1--;
count2--;
}
// 计数阶段
// 找到了两个候选人之后,需要确定票数是否满足大于 N/3
count1 = 0;
count2 = 0;
for (int num : nums) {
if (can1 == num) count1++;
else if (can2 == num) count2++;
}
if (count1 > nums.length / 3) result.add(can1);
if (count2 > nums.length / 3) result.add(can2);
return result;
}
}