力扣算法之摩尔投票

算法抽象

摩尔投票算法核心思想:根据条件划分不同阵营,不同阵营成员相互抵消,最后不能抵消的成员为同阵营成员。

分析

  1. 摩尔投票算法因为最后存活名单与数据规模无关,故它的空间复杂度为O(1);
  2. 摩尔投票算法最后的存活名单是抵消次数最多的前几位成员;
  3. 摩尔投票隐藏有三个重要核心(一个条件,两个名单)—抵消条件,存活名单与计数名单:抵消条件用来判断抵消,存活名单用来记录抵消的成员,计数名单用来实现抵消功能

过程抽象

在一个数组规模为n中,求出现次数大于[n/k]的成员.
首先明确,数组规模n,出现次数大于[n/k]的成员最多有k-1个,可以确定名单规模为k-1.
当k=2时数据抽象
如图:
摩尔算法K=2过程
当k=3时数据抽象
摩尔投票K=3过程
扩展到k时,我们可以归纳出抵消过程中以下特点

  1. 当检索命中时,命中位计数加1;
  2. 检索未命中时:
    *1. 有计数为0的无效位,将该位置为命中数,相应计数加1;
    *2. 无计数为0的无效位,所有名单成员与命中相互抵消,所有计数都减1.

应用场景

LeetCode题

  1. 求数组规模为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;

    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值