数组中出现次数超过一半的算法_认识摩尔投票法

JZ39

题目描述:

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

限制: 1 <= 数组长度 <= 50000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目分析

  1. 统计数组中的元素, 首先可以想到哈希表, 需要创建一个哈希表,空间复杂度为O(n); 并且需要将数组的元素遍历一遍, 输入哈希表中, 时间复杂度是O(n).
  2. 将数组中的数据重新排序,最中间的数就是我们要找的众数, 使用排序算法, 时间复杂度有点长.
  3. 摩尔投票法 : 本题最优的方法

WHAT is 摩尔排序法

博耶-摩尔多数投票算法(Boyer–Moore majority vote algorithm),中文常作多数投票算法摩尔投票算法等,是一种用来寻找一组元素中占多数元素的常数空间级时间复杂度算法。这一算法由罗伯特·S·博耶和J·斯特罗瑟·摩尔在1981年发表,也是处理数据流的一种典型算法。

这一算法应用的问题原型是在集合中寻找可能存在的多数元素,这一元素在输入的序列重复出现并占到了序列元素的一半以上;在第一遍遍历之后应该再进行一个遍历以统计第一次算法遍历的结果出现次数,确定其是否为众数;如果一个序列中没有占到多数的元素,那么第一次的结果就可能是无效的随机元素。对于数据流而言,则不太可能在亚线性空间复杂度的情况下中就寻找到出现频率最高的元素;而对于序列,其元素的重复次数也有可能很低。

  摩尔投票法, 总的来说就是遍历数组, 如果是众数就投+1, 如果不是众数就投-1, 众数的数量大于数组的一半长度,所以最后众数投票的结果肯定是大于0的.
  
  上面是摩尔投票法的第一个性质,摩尔投票法还有一个重要性质:
  
  如果数组前几个元素的投票结果为0, 那么剩余元素最终的票数 和 总的票数相等.

  想找到众数, 从头开始假设一个元素为众数,然后投票,:
  第一个数是众数,好,计数 i + 1,然后看下一个元素;下一个元素如果是众数就 +1, 不是众数就 -1;
  每次循环判断i 是否等于0, 如果等于,就舍弃掉前面的计数结果, 接着讲下一个遍历到的元素假设为众数,重复上面的步骤
  如果这个数组里面确实存在众数, 那么计数的结果肯定是大于1的,并且最后假设的数,也就是返回的数,就是我们要寻找的众数。

问题:为什么随便假设众数,前面几个元素投票结果是0,也可以舍弃前面的技术结果?

  理想的投票:
在这里插入图片描述

  假设众数的投票:
在这里插入图片描述

  可以看出来,两种投票方法得到的结果是一样的,假设众数的投票省去了每一次选择不同元素进行假设的时间。

bug:返回的数不是众数?

  比如我们将输入稍微改变一下:
在这里插入图片描述
  这样输出的结果众数是3, 但是显然3不是众数, 数组根本就没有众数。

  所以,如果题目不像本题一样,一定存在众数,那么需要在后面对结果进行检验,遍历一遍数组,进行摩尔投票法,如果结果正确,返回的结果必定大于0,如果错误,则结果返回0。

摩尔投票法的算法流程:

在这里插入图片描述

本题代码

1. 数组中确定有众数

直接寻找即可:

class Solution {
    public int majorityElement(int[] nums) {
        int x = 0;
        int votes = 0;

        for(int num : nums){
            if(votes == 0) x = num;
            votes += num == x ? 1 : -1;
        }

        return x;
    }
}

2. 数组中不知道有没有众数

需要添加一个验证的过程,当没有众数的时候,返回0。

class Solution {
    public int majorityElement(int[] nums) {
        int x = 0;
        int votes = 0;
        int count = 0;

        for(int num : nums){
            if(votes == 0) x = num;
            votes += num == x ? 1 : -1;
        }

        // 验证x是否为众数?
        for(int num : nums){
        	if(num == x) count++;
        }
        return count > nums.length / 2 ? x : 0;// 当无众数时, 返回0
    }
}

  
  
  
  
  

买了个ATLI EON玩玩。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值