算法:多数元素(Java语言)

算法:多数元素


前言

柚子下面展示多数元素的几种解法,感兴趣的话,快向下看吧!


提示:以下是本篇文章正文内容,下面案例可供参考

一、解法一(哈希)

HashMap+分别两次循环
class Solution {
    public int majorityElement(int[] nums) {
      int middle = nums.length / 2;
        //key是数组值,value是值出现的次数
        Map<Integer, Integer> numMap = new HashMap<>();
        for (int num : nums) {
            if (!numMap.containsKey(num)) {
                numMap.put(num, 1);
            } else {
                numMap.put(num, numMap.get(num) + 1);
            }
        }
        int num = 0;
        for (Map.Entry<Integer, Integer> entry : numMap.entrySet()) {
            if (entry.getValue() > middle) {
                num = entry.getKey();
            }
        }
        return num;
    }
}

在这里插入图片描述

二、解法二(哈希+打擂台遍历)

HashMap+打擂台遍历(维护最大的值)
class Solution {
    private Map<Integer, Integer> countNums (int[] nums) {
        Map<Integer, Integer> numMap = new HashMap<>();
        for (int num : nums) {
            if (!numMap.containsKey(num)) {
                numMap.put(num, 1);
            } else {
                numMap.put(num, numMap.get(num) + 1);
            }
        }
        return numMap;
    }
    //hashMap+打擂台遍历(维护最大的值)
    public int majorityElement(int[] nums) {
        Map<Integer, Integer> counts = countNums(nums);
        Map.Entry<Integer, Integer> majorityEntry = null;
        for(Map.Entry<Integer, Integer> entry : counts.entrySet()) {
	               if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) {
                majorityEntry = entry;
            }
        }
        return majorityEntry.getKey();
    }
}

在这里插入图片描述

三、解法三(排序)

因为数组定存在n/2长度的多数元素,取排序后的n/2下标定为正确值
class Solution {
    public int majorityElement(int[] nums) {
        int middle = nums.length / 2;
        Arrays.sort(nums);
        return nums[middle];
    }
}

在这里插入图片描述

四、解法四(随机)

随机化,因为众数出现的次数>n/2,所以随机取值有很大概率取到众数
class Solution {
     //生成随机数
    private int randRange(Random random, int min, int max) {
        return random.nextInt(max - min) + min;
    }
    //计数发生次数
    private int countOccurences(int[] nums, int num) {
        int count = 0;
        for (int num1 : nums) {
            if (num1 == num) {
                count++;
            }
        }
        return count;
    }

    //返回众数
    public int majorityElement(int[] nums) {
        Random random = new Random();
        //算出中间下标
        int middle = nums.length/2;
        while (true) {
            //获取随机数
            int candidate = nums[randRange(random, 0, nums.length)];
            //遍历随机数的发生次数 和 中间下标比较
            if (countOccurences(nums, candidate) > middle) {
                return candidate;
            }
        }
    }
}

在这里插入图片描述

五、解法五(分治)

如果数 a 是数组 nums 的众数,如果我们将 nums 分成两部分,那么 a 必定是至少一部分的众数。
我们可以使用反证法来证明这个结论。假设 a 既不是左半部分的众数,也不是右半部分的众数,那么 a 出现的次数少于 l/ 2 + r/2 次,
其中 l 和 r 分别是左半部分和右半部分的长度。由于 l/2 + r/2 <= (l + r) / 2,说明 a 也不是数组 nums 的众数,因此出现了矛盾。
所以这个结论是正确的。
这样以来,我们就可以使用分治法解决这个问题:将数组分成左右两部分,分别求出左半部分的众数 a1 以及右半部分的众数 a2,
随后在 a1 和 a2 中选出正确的众数。

算法:
我们使用经典的分治算法递归求解,直到所有的子问题都是长度为 1 的数组。长度为 1 的子数组中唯一的数显然是众数,直接返回即可。如果回溯后某区间的长度大于 1,我们必须将左右子区间的值合并。如果它们的众数相同,那么显然这一段区间的众数是它们相同的值。否则,我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数。

class Solution {

   /**
     * 统计众数的次数
     *
     * @param nums 数组
     * @param num  众数
     * @param lo   左边下标
     * @param hi   右边下标
     * @return 众数出现的次数
     */
    private int countInRange(int[] nums, int num, int lo, int hi) {
        int count = 0;
        for (int i = lo; i <= hi; i++) {
            if (nums[i] == num) {
                count++;
            }
        }
        return count;
    }

  
    /**
     * 计算众数
     *
     * @param nums 数组
     * @param lo   左边下标
     * @param hi   右边下标
     * @return 数组中的众数
     */
    private int majorityElementRec(int[] nums, int lo, int hi) {
        //大小为1的数组中唯一的元素是众数
        if (lo == hi) {
            return nums[lo];
        }
        //递归左边和右边部分
        //中间数=(右边下标-左边下标)/2 + 左边下标,根据mid,将数组分为左右两部分
        int mid = (hi - lo) / 2 + lo;
        int left = majorityElementRec(nums, lo, mid);
        int right = majorityElementRec(nums, mid + 1, hi);
        //如果左半边众数和右半边的众数相同,则将其返回
        if (left == right) {
            return left;
        }
        //如果左边众数跟右边众数不相同,选出出现次数更多的众数
        int leftCount = countInRange(nums, left, lo, hi);
        int rightCount = countInRange(nums, right, lo, hi);
        return leftCount > rightCount ? left : right;
    }

    public int majorityElement(int[] nums) {
        return majorityElementRec(nums, 0, nums.length - 1);
    }
}

在这里插入图片描述

六、解法六(Boyer-Moore 投票算法)

Boyer-Moore 投票算法(因为解析比较长,可以看另外一篇文章)

传送门: Boyer-Moore 投票算法解析

class Solution {  
    public int majorityElement(int[] nums) {  
        //初始count为0  
        int count = 0;  
        //候选众数为null  
        Integer candidate = null;  
        //遍历nums数组  
        for (int num : nums) {  
            //count为0时,就让候选众数为当前数组元素  
            if (count == 0) {  
                candidate = num;  
            }  
            //如果数组元素跟候选众数相等,count加1,反之count就减1  
            count += (num == candidate) ? 1 : -1;  
        }  
        //因为最后遍历结束后,count非负,且最后的候选众数就是数组的众数  
        return candidate;  
    }  
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/34416007194d4765a713f76327d12578.png

题目来源:https://leetcode.cn/leetbook/read/top-interview-questions/xm77tm/


╭◜◝ ͡ ◜◝╮
( ˃̶͈◡˂ ̶͈ )感觉有用的话,欢迎点赞评论呀!
╰◟◞ ͜ ◟◞╯

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柚子猫屿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值