Problem Description
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋
的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
e.g.
示例 1:
- 输入:
[3,2,3]
- 输出:
3
- 输入:
示例 2:
- 输入:
[2,2,1,1,1,2,2]
- 输出:
2
- 输入:
Solution
1. Hash表
首先最容易想到的,利用Hash表:
1 | public static int majorityElement(int[] nums) { |
key
来保存数组的元素,value
来保存出现的次数。
因为众数的个数一定是大于n/2的,所以只要取出value
大于n/2的key
即可。
2. 随机流&暴力流
1 | public static int thirdMethod(int[] nums) { |
众数在题干中明确说明了个数是n/2,一定是占据一半以上的,所以随机抽取一个元素,较大可能性是众数。统计该元素的个数,符合个数大于n/2即可。一开始设置第一个元素为众数统计个数,全部遍历后如果不符合,则设置第二个元素为基准继续遍历。这样其实就和双for循环没什么区别了,不具随机性。
我一开始做法就是设置第一个元素为基准,这样写的最大时间复杂度是平方级 O(n2),一旦众数全部位于数组的后半段,那么时间会超久,所以我这样提交,系统判定超出时间限制。
官方题解是随机设置一个基准,理想情况是众数较多,很容易抽中,较少的次数可以完成推断,但是存在一种极限情况,就是每次随机基准都抽不到众数,那么会变成无穷尽的计算。最坏情况的时间复杂度为 O(∞)。
3. 排序流
1 | public static int secondMethod(int[] nums) { |
充分利用了n/2的特性,将数组排序后,无论数组的长度是奇数还是偶数,第nums.length / 2
一定是众数。
4. 投票流
该方法个人认为最有意思和最有拓展性。假设这样一个数组:
[7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 7, 7, 7, 7]
- 初始化被选举人(elector)为第一个数,设定count = 1,然后开始遍历;
- 当nextValue == elector,count + 1,反之 - 1;
- 当count == 0时,且存在nextValue时,nextValue成为下一个elector,count = 1;
- 最终的选举人一定是那个众数
因为众数的个数一定是大于其它数字的和,所以相减一定大于0,最后剩下的被投票的那个元素一定是众数。
所以上述的数组,票数就是[1, 2, 1, 2, 1, 0 | 1, 0 | 1, 2, 1, 0 | 1, 2, 3, 4
遍历到数组的最后阶段,elector是7
,后面票数越来越多,也不会被更换elector了。即使是众数都在前面
[7, 7, 7, 7, 7, 1, 5, 5 ]
,众数的和也够减去后半段的其他元素的总和而保证elector不被更换。
该思想不复杂实现起来非常简单:
1 | public static int boyerMoore(int[] nums) { |