Leetcode——求众数1,2(多数元素)n/2, n/3

1. 求众数1 (多数元素)

在这里插入图片描述

(1)Map

遍历一遍数组:key表示数组中的某个数,value表示该数出现了几次,当统计到某数出现的次数大于数组一半时返回该数。

class Solution {
    public int majorityElement(int[] nums) {
        Map<Integer,Integer> ant = new HashMap<>();
        for (int num : nums) {
            ant.put(num, ant.getOrDefault(num, 0) + 1);
            if (ant.get(num) > nums.length / 2) 
            	return num;
        }
        return 0;
    }
}

(2)排序

因为答案占了一般多,排序一下,直接返回中间位置就是答案了。

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

(3)投票法

使用摩尔投票法

  • 如果是同一党派,票数+1
  • 如果是不同党派,抵消,票数-1
  • 要是票数归零,重新指定候选人,初始化票数为1
class Solution {
    public int majorityElement(int[] nums) {
        // 初始化候选人
        int major = nums[0];
        // 记录票数
        int count = 1;
        for (int i = 1; i < nums.length; i++) {
            if (count == 0) {
                //前面都消完了,在重新指定候选人
                count++;
                major = nums[i];
            } 
            else if (major == nums[i]) {
                //自己人票数加1
                count++;
            } 
            else {
                // 不是自己人就消掉一个
                count--;
            }
        }
        return major;
    }
}

2. 求众数2

在这里插入图片描述

(1)HashMap

时间:O(N)
空间:O(N)
接定义一个哈希表。遍历所有元素,统计其频率。之后再遍历哈希表中所有数到频率的映射对,如果频率大于N/3,则加入结果res。最后返回res即可。

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        List<Integer> res = new ArrayList<>();
        int len = nums.length;
        if (nums == null || len < 1)
            return res;

        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }

        for (int key : map.keySet()) {
            if (map.get(key) > len / 3)
                res.add(key);
        }

        return res;
    }
}

(2)摩尔投票法

时间:O(N)
空间:O(1)

投票法的理解很重要,其本质就是消除,遇到三个不同的就消除。最终保留的元素中一定包含频率最大的元素(多的元素顶得住起消除)循环结束之后没有被消除的就是频率比较高的。但是不一定满足 频率大于n//3因此需要统计一下才可以。

以[1,1,2,2,3,3,3]为了例子
在遍历到第一个3之前各个变量的状态为
num1 = 1,count1 = 2
num2 = 2,count2 = 2
num = 3(第一个3)时,出现第一个与num1、num2都不同的数,于是消除三个数(真的类似于消消乐,但是遇到3个不同的才消一下)。
就有:
num1 = 1, count1 = 2-1 = 1
num2 = 2, count2 = 2-1 = 1
同理 num=3(第二个3)依然是三个不同的数,继续消除。
num1 = 1, count1 = 1-1 = 0
num2 = 2, count2 = 1-1 = 0
继续 num = 3(第三个3),此时虽然与num1,num2不同,但是count1,count2 均为0。于是更新num1
num1 = 3, count1 = 1
num2 = 2, count2 = 0(保持不变)
循环结束。
可见
最终保留的 num1、num2 是否满足结果,需要进一步的判断才OK
num1 和 num2 也不会相等

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        List<Integer> res = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return res;
        }
        // 定义两个候选者和它们的票数
        int cand1 = 0,cand2 = 0;    
        int cnt1 = 0, cnt2 = 0;
        // 投票过程
        for (int num : nums) {
            // 如果是候选者1,票数++
            if (num == cand1) {
                cnt1++;
                // 一遍遍历,如果你不想写continue,你写多个else if也可以
                continue;
            }
            // 如果是候选者2,票数++
            if (num == cand2) {
                cnt2++;
                continue;
            }
            // 既不是cand1也不是cand2,如果cnt1为0,那它就去做cand1
            if (cnt1 == 0) {
                cand1 = num;
                cnt1++;
                continue;
            }
            // 如果cand1的数量不为0但是cand2的数量为0,那他就去做cand2
            if (cnt2 == 0) {
                cand2 = num;
                cnt2++;
                continue;
            }
            // 如果cand1和cand2的数量都不为0,那就都-1
            cnt1--;
            cnt2--;
        }
        // 检查两个票数符不符合
        cnt1 = cnt2 = 0;
        for (int num : nums) {
            if (num == cand1) {
                cnt1++;
            } else if (num == cand2) {  
                // 这里一定要用else if
                // 因为可能出现[0,0,0]这种用例,导致两个cand是一样的,写两个if结果就变为[0,0]了
                cnt2++;
            }
        }
        int n = nums.length;
        if (cnt1 > n / 3) {
            res.add(cand1);
        }
        if (cnt2 > n / 3) {
            res.add(cand2);
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Yawn__

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

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

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

打赏作者

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

抵扣说明:

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

余额充值