左神讲算法——超级水王问题(详解)

本文根据左神课程学习总结而来2021最新左神数据结构算法全家桶

超级水王问题:给你一个数组,出现次数大于数组长度的一半的元素称之为水王数,怎么能快速找到水王数?

内存限制:时间复杂度O(n),额外空间复杂度O(1)——也就是遍历数组的次数为有限次,申请的变量数为有限个

方式一:暴力法

暴力法的思想很简单,用一个hashmap来存放数组中每个元素及其对应的数量,最后遍历hashmap判断有无元素数量大于数组长度一半,有则直接返回水王数,否则返回-1

public static int verify(int[] arr) {
    if (arr == null || arr.length == 0)
        return -1;
    //新建hashmap用于存放数组中每个元素及其对应的数量
    HashMap<Integer, Integer> hashMap = new HashMap<>();
    //遍历数组的每个元素
    for(int num:arr){
        //如果hashmap中包含该元素,则将该元素的数量+1
        if(hashMap.containsKey(num))
            hashMap.put(num,hashMap.get(num)+1);
        //如果hashmap中不包含key,则将该元素的数量置为1
        else
            hashMap.put(num,1);
    }
    //遍历hashmap中每一个entry<k,v>
    for(Map.Entry<Integer,Integer> record:hashMap.entrySet()){
        if(record.getValue()>(arr.length>>1))//这里采用位运算,速度快
            //返回水王数
            return record.getKey();
    }
    return -1;
}

显然,该方法不符合题目的限制,由于使用了hashmap,所以额外的空间复杂度为O(N)


方法二

如何使用有限变量遍历有限次数来完成呢?

思路:从数组第一个元素开始,依次删掉两个不同的数,最后如果有水王数的话,它会剩下来(反过来不成立,也就是剩下来的数不一定是水王数)
image-20210610130624868
如上图所示,依次删除两个不同的元素,相同则不删除,最后只剩下3即是水王数

但请注意,剩下的不一定是水王数,比如:12345,删掉12、34还剩5,但是5不是水王数

这是什么原理呢?我们假设一个数组中有水王数,由于水王数的个数大于所有元素个数的一半,所以如果拿一个非水王数抵消一个水王数,最后还是会剩下水王数;上述删除不同元素的过程就是如此,还可能会存在两个非水王数互相抵消的情况,这样水王数剩下的会更多。

因此我们的算法可以转换为以下两步:

  1. 依次删除数组中两个不同的数
  2. 判断是否有数剩余。如果没有数剩余,则不存在水王;如果有数剩余,我们再遍历一次数组,计算该数的真实的出现次数与数组长度的一半进行比较判断是否为水王数

怎么实现呢?我们只需要两个变量,变量一candidate作为候选用来存放可能的水王数,变量二hp用来存放其剩余个数,按如下规则遍历数组:

  • hp=0 代表没有candidate候选数;hp>0 代表有candidate候选数
  • 如果没有candidate,则设置当前数赋予candidate,即将当前数作为候选数,hp设置为1
  • 如果有candidate,判断当前数是否等于candidate,相等则hp+1,不相等则hp-1

最后判断看hp是否为0,如果不为0,则代表有可能的水王数,再进行计数判断即可

代码实现

public static int waterKing(int[] arr) {
    if (arr == null || arr.length == 0)
        return -1;
    int candidate = 0;
    int hp = 0;
    for (int cur : arr) {
        if (hp == 0) {//没有候选,则将当前数设为候选
            candidate = cur;
            hp++;
        } else if (cur != candidate)//有候选且当前数!=侯选数
            hp--;
        else//有候选且当前数=候选数
            hp++;
    }
    //没有候选数剩余,则没有水王数
    if (hp == 0)
        return -1;
    //计算候选数在数组中的元素个数
    int count = 0;
    for (int num : arr) {
        if (num == candidate)
            count++;
    }
    //如果候选数元素个数>数组长度的一半则返回,否则返回-1
    return count > (arr.length >> 1) ? candidate : -1;
}

  • 15
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Baret-H

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

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

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

打赏作者

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

抵扣说明:

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

余额充值