【算法】数组中出现次数超过一半的数字

数组中出现次数超过一半的数字_牛客题霸_牛客网

 这道题给出3种做法,咱们一个个来~


【方法1】用Map统计次数

思路:既然它要找出现次数超过一半的数字,那就得用到统计对吧,那必然想到Map啊,这玩意就是为统计而生的(瞎说的,我也不懂)

我们只需要遍历完整个数组,如果统计每个数字出现的次数即可

统计好每个数字出现的次数后,我们要求的是要超过数组的一半,此时我们再来循环一遍数组,看看i下标对应的值所统计的次数是不是超过数组长度一半的~~

下面是代码展示

import java.util.Map;
import java.util.HashMap;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        //方法1:HashMap
        if(array == null || array.length == 0){
            return 0;
        }

        int half = array.length / 2;
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0; i < array.length; i++){
            if(map.get(array[i]) == null){
                map.put(array[i],1);
            }else{
                int value = map.get(array[i]);
                map.put(array[i],value + 1);
                }
        }
        for(int i = 0; i < array.length; i++){
            if(map.get(array[i]) > half){
                return array[i];
            }
        }
        return 0;

    }
}

先判断数组是否合法,不合法返回0

接着定义一个HashMap,遍历数组,如果此时i下标的数字没有存在过Map中,就给它统计出现一次;否则就是已经出现过也统计过的数字了,此时只需要给它的value值更新一下就行了,再原来的基础上+1,就统计更新好了;用HashMap全部统计好后,出来遍历检查一下数组即可,只要此时i下标的数出现次数超过一半就直接return了,不需要再继续往下遍历了~(这也是Map的查找的优势~)


【方法2】排序后的数组的中间数字

这个要怎么理解呢?意思就是说假设你的数组里面存在出现次数超过数组一半的数字,当我们把这个数组按照从小到大排序好后,排序好后的数组的中间数字就必然是那个出现次数超过一半的数字~这个可以自己列举验证一下~

当我们拿到排序好后的的中间数字后,次数是不确定它是不是真的出现次数超过了数组的一半,所以我们还要再遍历一遍数组来判断拿到的这个数字是不是出现次数超过了数组长度的一半~

下面是代码展示

import java.util.Arrays;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        //方法2,排序后的中间数字
        if(array == null || array.length == 0){
            return 0;
        }
        int half = array.length / 2;
        Arrays.sort(array);
        int ret = array[half];

        int count = 0;//用来统计次数
        for(int i = 0; i < array.length; i++){
            if(array[i] == ret){
                count++;
                if(count > half){
                    return ret;
                }
            }
        }
        return 0;
    }
}

最后判断的那里说明一下,我们最后要定义一个count来统计这个中间数字出现的次数嘛,所以边统计我就边判断了,如果超过了一半就直接return了,不用非要全部遍历完,当然全部遍历完再判断count是不是大于half也行~


【方法3】消除法(面试推荐做法)

这个方法面试推荐,那自然说明这个方法不容易比上面两种方法好想到呗~

这个方法是遍历消除,听我娓娓道来~

假如我们现在给出一个有结果的数组: 1  2   2   2   3

我们现在看是直接知道了2是出现超过一半的数字,下面我们来说说怎么消除拿到2~

首先我们要明白,假如数组里面存在超过一半的数字,那这个数字个数一定是大与除了它之外所有数字的个数和的!!!!(这里自己细品理解一下再继续)

对不~

那我们想想,我们是不是拿这个数字跟出了它以外的数字一一相消掉,最后一定至少剩下一个这个数字?能理解吧?也就是3个2分别跟1和3消掉后,还会剩下一个2,这个2就是出现次数超过一半的数字(前提是你知道这个数组一定有出现次数超过一半的数,但有的数组不一定有)

所以我们就要用消除的方法来写这个题

下面是展示代码,然后再继续听我说

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        //方法3:消除
        if(array == null || array.length == 0){
            return 0;
        }

        int half = array.length / 2;
        int tmp = array[0];
        int times = 1;
        for(int i = 1; i < array.length; i++){
            if(array[i] == tmp && times != 0){
                times++;
            }else if(array[i] != tmp && times != 0){
                times--;
                
            }else if(times == 0){
                tmp = array[i];
                times = 1;
            }
        }
        //遍历完后拿到的tmp可能是对的结果,要再次遍历检查~
        int count = 0;
        for(int i = 0; i < array.length; i++){
            if(array[i] == tmp){
                count++;
            }
        }
        if(count > half){
            return tmp;
        }else{
            return 0;
        }
    }
}

我们首先定义一个tmp,这个tmp的初始值就是数组的首元素,然后定义一个times来记录这个tmp当前的次数~(我们把tmp形象的想象成一个仓库

我们遍历数组,从下标1开始遍历

如果遇到的下标i的数字不等于当前的tmp,那么times就减减,这就是不相同的两个数字的消除~

如果此时下标i的数字跟当前的tmp相等,那么times就++;相当于储备一下,为了之后的消除做准备

当然消除的过程中times会变成0,说明此时的tmp里面是没有值的,虽然tmp是一个数值,但是我们说了把它想象成一个仓库就是这个意思,仓库会有被清空的时候,times表示仓库里没东西了,所以times == 0 时,那么下一次遍历的数字就会往这个所谓的‘仓库’里存放,然后再继续遍历,两数不相同就相互抵消掉同时times要跟着减减,相同就++,times也要跟着++

到这里就结束了~

思路我说的很清楚了,可以多想几次,然后动手去做,按照思路去做,不动手光看是不会明白的,你要在朦朦胧胧的感觉就自己跟着思路去动手写,这样很快就能明白(可能写着写着就灵光一现了~)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值