1. 题目
https://leetcode.cn/problems/majority-element/
169. 多数元素
考察点
- 数组和哈希表的基本操作,
- 以及如何在线性时间和常数空间内找到数组中的多数元素。
- 多数元素是指在数组中出现次数大于⌊n/2⌋的元素
2. 解法
解法一:摩尔投票法
理论背景
- 摩尔投票法是一种用来解决绝对众数问题的算法,即在一个数组中找到出现次数超过一半的元素。
- 摩尔投票法的原理是:
- 每次从序列里选择两个不相同的数字删除掉(或称为“抵消”),
- 最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个。
- 摩尔投票法的过程
- 可以用一个候选人和一个计数器来表示,初始时候候选人为任意值,计数器为0。
- 遍历数组中的每个元素,如果计数器为0,就将当前元素设为候选人,并将计数器设为1;
- 如果当前元素与候选人相同,就将计数器加1;
- 如果当前元素与候选人不同,就将计数器减1。
- 最后剩下的候选人就是绝对众数(如果存在)。
- 摩尔投票法的时间复杂度是O(n),空间复杂度是O(1)
- 摩尔投票法的优点
- 是它可以在O(n)的时间复杂度和O(1)的空间复杂度下解决绝对众数问题,而其他方法可能需要排序或哈希表等额外的空间。
- 摩尔投票法也可以拓展到求多个候选人的情况,例如求出现次数超过1/3的所有元素。
class Solution { public List<Integer> majorityElement(int[] nums) { // 初始化两个候选人和两个计数器 int cand1 = nums[0], cand2 = nums[0]; int count1 = 0, count2 = 0; // 遍历数组 for (int num : nums) { // 如果当前元素与候选人相同,就增加对应的计数器 if (num == cand1) { count1++; continue; } if (num == cand2) { count2++; continue; } // 如果当前元素与候选人不同,且某个计数器为0,就更新对应的候选人和计数器 if (count1 == 0) { cand1 = num; count1++; continue; } if (count2 == 0) { cand2 = num; count2++; continue; } // 如果当前元素与候选人不同,且两个计数器都不为0,就减少两个计数器 count1--; count2--; } // 验证最后的候选人是否真的出现次数超过1/3 List<Integer> result = new ArrayList<>(); count1 = 0; count2 = 0; for (int num : nums) { if (num == cand1) { count1++; } else if (num == cand2) { count2++; } } int n = nums.length; if (count1 > n / 3) { result.add(cand1); } if (count2 > n / 3) { result.add(cand2); } return result; } }
- 摩尔投票法的缺点
- 是它需要验证最后的候选人是否真的是绝对众数,这需要再遍历一次数组,增加了一些时间开销。摩尔投票法也不能直接应用于数据流或者序列中没有绝对众数的情况,需要进行一些修改或者结合其他数据结构。
- 摩尔投票法的一大应用就是求众数,即在一个数组中找到出现次数超过一定比例的元素。
- 摩尔投票法可以用来解决一些选举或者统计的问题,例如在一个班里选副班长,或者在一个社区中找到最受欢迎的人。
- 摩尔投票法也可以结合线段树来解决区间绝对众数问题,即在一个数组的某个子区间中找到出现次数超过一半的元素。这种问题可以用来分析数据的变化趋势或者异常情况。
具体实现
public static int majority(int[] nums) { int currentNum = nums[0]; int cnt = 1; for (int i = 1; i < nums.length; i++) { if (cnt == 0) { currentNum = nums[i]; cnt++; } else { if (nums[i] == currentNum) { cnt++; } else { cnt--; } } } return currentNum; }
解法二: 排序法
class Solution { public int majorityElement(int[] nums) { // 对数组进行排序 Arrays.sort(nums); // 返回数组的中间元素 return nums[nums.length / 2]; } }