当一个序列中存在一个占大多数的的元素的时候(超过50%),该算法可以在O(1)空间和O(n)时间内找出这个元素。
- Tips: ⌊ 59/60⌋ = 0, floor(),向下取整
Majority Element
Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.
You may assume that the array is non-empty and the majority element always exist in the array.
Majority Element II
Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. The algorithm should run in linear time and in O(1) space.
Analysis
大致思路就是,比如超过数组一半元素以上的element,可以每次从数组删除2个不一样的元素,那么最后剩下的一定就是major element。
超过 ⌊ n/3 ⌋,那就每次从数组里面删除三个不一样的元素。
Code
Majority Element
public class Solution {
public int majorityElement(int[] nums) {
int count = 0;
int target =0 ;
for(int i=0; i<nums.length; i++){
if(count==0){
count++;
target = nums[i];
}
else if(nums[i]==target){
count++;
}
else{
count--;
}
}
return target;
}
}
Majority Element II
思路依然同 Majority Element 一样,不同的是我们需要两个 Majority Element 的候选者,同时需要两个 count 分别对候选者进行计数。
count 为 candidate 当前出现的次数。count == 0 说明当前 candidate 对应的候选者已经被移除,我们需要设定一个新的候选者。
public class Solution {
public List<Integer> majorityElement(int[] nums) {
List<Integer> result = new ArrayList<Integer>();
int count1 = 0, count2 = 0;
int target1 = 0 ,target2 = 1;
for(int i=0; i<nums.length; i++){
if(nums[i]==target1){//当前数字等于1号候选数字
count1++;
}
else if(nums[i]==target2){//当前数字等于2号候选数字
count2++;
}
else if(count1==0){ //当前数字不等于1,2号候选数字,且1号候选数字可以被重新选择
count1++;
target1 = nums[i];
}
else if(count2==0){//当前数字不等于1,2号候选数字,且2号候选数字可以被重新选择
count2++;
target2 = nums[i];
}
else{//删除三个互相不一样的元素
count1--;
count2--;
}
}
count1 = 0;
count2 = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target1)
count1++;
else if (nums[i] == target2)
count2++;
}
if (count1 > nums.length / 3)
result.add(target1);
if (count2 > nums.length / 3)
result.add(target2);
return result;
}
}
一些思考
以下部分via Majority Vote Alogrithm(最大投票算法)及其扩展
问题一: if 的判定顺序有要求吗?如果有要求的话应该是怎么样的呢?
答案是有要求,细心的读者可能发现,在 Majority Element 中,我们对 count == 0 的判断在对 candidate == nums[i] 的判断之前,而在 Majority Element II 中则正好相反。
这是因为,count == 0 是用来判断对应 candidate 的当前存活量,在判断这一步之前,我们必须确保数组中当前数字不等于 两个 candidate中的任意一个。否则,我们可能会在 count0!=0 && count1==0 && nums[i]==candidate0 时错误的将 nums[i] 赋值给 candidate1。
问题二:这里给 candidate0 candidate1 初始化值为 0,这会不会影响我们运行的结果?
不会,因为 candidate0 只会在第一次循环中使用,如果 candidate0 == nums[0],count++不会引起任何问题。如果 candidate != nums[0] 那么我们此时 count==0 重新初始化 candidate0 == nums[0],同样不会有任何影响。
问题二扩充:如果我们初始化 int candidate0 = 0, candidate1 = 1 会不会影响我们的运行结果呢?
问题三:这里能够省略 distinct() 吗?为什么?
不能,尽管我们在循环中首先通过 if(candidate0 == nums[i]) 和 else if(candidate1 == nums[i]) 两个 if 判断,使得 candidate0 != candidate1 在绝大部分下成立,但是在一种极为特殊的情况下仍然可能会使得我们得到重复的数组。
试想当整个数组所有的数字都相等的时候,我们 candidate0 和 candidate1 这两个候选数字中,有一个数字将永远不会被重新赋值,也就是说,有一个数字将我们赋给的初值保持到了最后。
在我们的代码中,因为我们将两个候选数字都初始化 0,所以当数组 全为0 时会返回错误的结果。
这一点,我们可以通过将两个候选数字初始化为不同的数字来解决:int candidate0 = 0,candidate1 = 1,这样我们就可以移除掉 distinct() 了