算法抽象
摩尔投票算法核心思想:根据条件划分不同阵营,不同阵营成员相互抵消,最后不能抵消的成员为同阵营成员。
分析
- 摩尔投票算法因为最后存活名单与数据规模无关,故它的空间复杂度为O(1);
- 摩尔投票算法最后的存活名单是抵消次数最多的前几位成员;
- 摩尔投票隐藏有三个重要核心(一个条件,两个名单)—抵消条件,存活名单与计数名单:抵消条件用来判断抵消,存活名单用来记录抵消的成员,计数名单用来实现抵消功能。
过程抽象
在一个数组规模为n中,求出现次数大于[n/k]的成员.
首先明确,数组规模n,出现次数大于[n/k]的成员最多有k-1个,可以确定名单规模为k-1.
当k=2时数据抽象
如图:
当k=3时数据抽象
扩展到k时,我们可以归纳出抵消过程中以下特点
- 当检索命中时,命中位计数加1;
- 检索未命中时:
*1. 有计数为0的无效位,将该位置为命中数,相应计数加1;
*2. 无计数为0的无效位,所有名单成员与命中相互抵消,所有计数都减1.
应用场景
LeetCode题
- 求数组规模为n中出现次数大与[n/k]次的元素,要求空间复杂度为0(1)
*1. 提取抵消条件为不相等;
*2. 提取存活名单和计数名单规模为k-1;
*3. 根据摩尔投票过程算法确定最后的存活名单;
*4. 最后在存活名单中筛选符合条件的成员。
169. 多数元素
链接: 169. 多数元素.
题目描述
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
Java代码实现
/**标签:摩尔投票,空间复杂度O(1)
* 169. 多数元素
* 描述:
* 给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
*
* 你可以假设数组是非空的,并且给定的数组总是存在多数元素。
*
*
*/
public class Question169 {
public int majorityElement(int[] nums) {
//存活名单[a]
int a=-9999999;
//存活计数[count]
int count = 0;
for(int i :nums)
{
//未命中有无效位的情况
if(count==0&&++count>=0) a=i;
//命中情况
else if(count!=0&&a==i) count++;
//未命中无无效位的情况
else count--;
}
return a;
}
}
}
229.求众数 II
链接: 229.求众数 II.
题目描述
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
示例 1:
输入:[3,2,3]
输出:[3]
示例 2:
输入:nums = [1]
输出:[1]
示例 3:
输入:[1,1,1,3,3,2,2,2]
输出:[1,2]
提示:
1 <= nums.length <= 5 * 104
-109 <= nums[i] <= 109
进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。
Java代码实现
import java.util.ArrayList;
import java.util.List;
/**标签:摩尔投票,空间复杂度O(1),求出现超过n/k的次数
* 229.求众数 II
*描述:
* 给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
* 示例:示例 1:
*
* 输入:[3,2,3]
* 输出:[3]
* 示例 2:
*
* 输入:nums = [1]
* 输出:[1]
* 示例 3:
*
* 输入:[1,1,1,3,3,2,2,2]
* 输出:[1,2]
*
* 提示:
* 1 <= nums.length <= 5 * 104
* -109 <= nums[i] <= 109
*/
public class Question229 {
public List<Integer> majorityElement(int[] nums){
//存活名单[a,b]
int a=0;int b=0;
//存活计数[c1,c2]
int c1=0; int c2=0;
for(int i : nums)
{
//命中情况
if(a==i&&c1!=0) c1++;
else if (b==i&&c2!=0) c2++;
//未命中有无效位情况
else if (c1==0 && ++c1>=0)a=i;
else if (c2==0 && ++c2>=0)b=i;
//未命中无无效位抵消情况
else
{
c1--;c2--;
}
}
//再从存活名单筛选符合条件成员
c1=0;c2=0;
for(int i:nums)
{
if(a==i)c1++;
else if (b==i)c2++;
}
List<Integer> list = new ArrayList<>();
int length = nums.length;
if(c1>length/3) list.add(a);
if (c2>length/3) list.add(b);
return list;
}
}