题目
Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times.
Note: The algorithm should run in linear time and in O(1) space.
Example 1:
Input: [3,2,3]
Output: [3]
Example 2:
Input: [1,1,1,3,3,2,2,2]
Output: [1,2]
思路
自己只想到使用HashMap记录次数,代码如下。
class Solution {
public List<Integer> majorityElement(int[] nums) {
List<Integer> res = new LinkedList<>();
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
if (map.get(num) > nums.length / 3 && !res.contains(num)) res.add(num);
if (res.size() == 2) return res;
}
return res;
}
}
但是题目提示使用O(1)空间。使用Boyer-Moore Majority Vote Algorithm,思路如下。
根据题目要求最多只可能有两个结果,记录为cand1,cand2,初始化为数组中两个不同的值。该算法的核心是删除两个不同的数不影响majority number,分以下几种情况:
1.删除的两个都是majority number
因为有
a
>
n
u
m
s
.
l
e
n
g
t
h
/
3
a > nums.length / 3
a>nums.length/3我们可以得到
a
−
1
>
(
n
u
m
s
.
l
e
n
g
t
h
−
3
)
/
3
a - 1 > (nums.length - 3) / 3
a−1>(nums.length−3)/3,减3是因为去掉了两个数和引发该情况的数(一个遍历的时候和这两个majority number不同的数)
e.g.
[
1
,
2
,
3
,
2
,
1
]
[1,2,3,2,1]
[1,2,3,2,1] 这个例子在3的时候进入代码将1和2的计数减去1,但是1和2还是
[
2
,
1
]
[2,1]
[2,1]的majority number
2.删除的两个都不是majority number,显然无影响
3.删除的一个是一个不是,就是一二情况的结合
e.g.
[
2
,
1
,
4
,
5
,
2
]
[2,1,4,5,2]
[2,1,4,5,2] 这个例子在4的时候将2和1的计数减去1,此时2仍是
[
5
,
2
]
[5,2]
[5,2]的majority element,虽然在5的时候cand1从2换成了5,但是最后的结果中cand仍然包含2(cand2=2)
从上面的例子也可以看出,在算法的结尾,只能保证majority element在cand中但是cand并不一定全是,所以还要重新遍历返回答案。
参考Leetcode给出的sample代码如下:
class Solution {
public List<Integer> majorityElement(int[] nums) {
List<Integer> result = new ArrayList<>();
if(nums == null || nums.length == 0)
return result;
int cand1 = 0, cand2 = 0;
int count1 = 0, count2 = 0;
for(int num : nums){
if(num == cand1){
count1 ++;
continue;
}
if(num == cand2){
count2 ++;
continue;
}
if(count1 == 0){
cand1 = num;
count1 = 1;
continue;
}
if(count2 == 0){
cand2 = num;
count2 = 1;
continue;
}
count1 --;
count2 --;
}
count1 = 0;
count2 = 0;
for(int num : nums){
if(num == cand1){
count1 ++;
}else if(num == cand2){
count2 ++;
}
}
if(count1 > nums.length / 3)
result.add(cand1);
if(count2 > nums.length / 3)
result.add(cand2);
return result;
}
}