【刷题日常】两道力扣题目,搞懂摩尔投票算法

169.多数元素

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素
你可以假设数组是非空的,并且给定的数组总是存在多数元素

  • 哈希表解法

key为不同的元素,value为出现的次数,遍历整个数组将key和value存入map中,再遍历每一个key的value,当value大于n/2时加入list中

    public int majorityElement(int[] nums) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int n : nums){
            int val = map.get(n)==null ? 0 : map.get(n);
            map.put(n,++val);
        }
        int ans = 0;
        for(int i : map.keySet()){
            if(map.get(i)>nums.length/2){
                ans = i;
                break;
            }
        }
        return ans;
    }

哈希表解法的时间复杂度为O(n),空间复杂度为O(n),力扣执行情况如下

  • 摩尔投票算法

举个例子,有六只羊和三只老虎、两只狼关在一起,假设每只老虎和狼只能吃一只羊,并且它们一定会吃羊,吃掉这只羊就会睡觉,要从这些动物中选出羊,那么这个动物一定是活着的不睡觉的动物,这就是投票算法的核心思想,一个一个抵消掉,多于一半的一定会剩下
现在对这些动物进行处理,找出真正的羊的编号,假设把羊编号设为1,老虎设为2,狼设为3,设置一个空间,把这些动物一只一只扔进去,并设置计数器,当编号与之前不符的则让计数器减一,并且同时判断计数器值,当计数器为零时,设置此时扔进去的动物编号为目标编号,我们可以注意到,狼只有2只,假设前两次扔进去的都是狼,目标则为狼,值为2,计数器变成2,再扔进去老虎,计数器会在第三次扔老虎的时候变成零,导致目标变成老虎,值为3,再扔进去六只羊,同理计数器会再次清零,让目标变成羊,值为1。接下来我们来代码实现

首先取count计数,targer为目标值,将这两个变量都声明为0,遍历数组,如果count为0时target等于目前的元素,再对count进行计算,若target等于目前元素count加一,若target不等于目前元素count减一

    public int majorityElement(int[] nums) {
        int count = 0;
        int target = 0;
        for(int num : nums){
            if(count == 0){
                target = num;
            }
            count += target == num ? 1 : -1;
        }
        return target;
    }

投票算法的时间复杂度为 O(n)、空间复杂度为 O(1) ,力扣执行情况如下
在这里插入图片描述

  • 排序法

还有一种比较简单的算法,这里也罗列出来,对这些数据进行排序,取下标n/2的元素,若这个元素出现次数大于n/2,那么在下标n/2的位置上一定是该元素

    public int majorityElement(int[] nums) {
       Arrays.sort(nums);
       return nums[nums.length/2];
   }

229.求众数

给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

  • 哈希表解法

这道题也可以使用哈希表解法,解法类似上题,这里直接展示代码

	public List<Integer> majorityElement(int[] nums) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            Integer val = map.get(nums[i]) == null ? 0 : map.get(nums[i]);
            map.put(nums[i],++val);
        }
        List<Integer> list = new ArrayList<>();
        for(Integer key : map.keySet()){
            if(map.get(key)>nums.length/3){
                list.add(key);
            }
        }
        return list;
    }

力扣执行情况如下
在这里插入图片描述

  • 摩尔投票算法

首先,我们可以先想一下出现超过 ⌊ n/3 ⌋ 的元素有几个,假如有三个,那么这三个元素出现的次数总和就会超过数组的长度,与题意不符,所以只能有两个,以此类推,出现超过n/k的元素个数一定最多为k-1个
已知超过n/3的元素个数只有两个,那么我们可以声明两个计数器两个目标值,使用第一种的投票方法找到两个出现次数最多的元素,再对数组遍历计数,如果大于n/3则加入list返回

投票方法简述:
声明a,计数器为c1,声明b,计数器为c2,遍历所有的元素,并判断计数器的值,若a与元素相等,则c1加一,若b与元素相等,则c2加一,当计数器为零时替换元素,计数器等于1,若这个元素不等于a也不等于b,则两个计数器都减一

    public List<Integer> majorityElement(int[] nums) {
        int a = 0;
        int b = 0;
        int c1 = 0;
        int c2 = 0;
        for(int i : nums){
            if(c1 != 0 && a == i){c1++;}
            else if(c2 != 0 && b == i){c2++;}
            else if(c1 == 0){
                a = i;
                c1++;}
            else if(c2 == 0){
                b = i;
                c2++;
            }else{
                c1--;
                c2--;
            }
        }
        c1=0;
        c2=0;

        List<Integer> list = new ArrayList<>();
        for(Integer n : nums){
            if(a == n){
                c1++;
            }else if(b == n){
                c2++;
            }
        }
        if(c1 > nums.length/3){
            list.add(a);
        }
        if(c2 > nums.length/3){
            list.add(b);
        }
        return list;
    }

以[1,1,1,3,3,2,2,2]为例记录数字变化情况

元素11133222
a11111111
c112333210
b00033332
c200012101

虽然c1变成0了,但循环结束了,所以a的值不会再进行替换

时间复杂度为O(n),空间复杂度为O(1),力扣执行情况如下
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值