剑指 Offer 39. 数组中出现次数超过一半的数字

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

限制:

1 <= 数组长度 <= 50000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof

1:我的题解:排序

看到这个题,我想了一会,嗯,然后我笑了 ,这tm排个序,中间的数肯定是出现超过一半的数字啊。。笑着笑着我就哭了,做了这么久的题,沃特么就只想到一个排序,这是要被面试官说等通知的节奏呀,起码排序得自己写吧。。。。。。

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

在这里插入图片描述

我们来分析一下Arrays.sort();
在这里插入图片描述源码就不贴了,有点长,应该是个快排吧,好像可以指定区间排序样

2:哈希表

class Solution {
    public int majorityElement(int[] nums) {
       if(nums.length==1)
       {
           return nums[0];
       }
        Map<Integer,Integer>map=new HashMap<>();
        for(int i=0;i<nums.length;i++)
        {
            if(map.containsKey(nums[i]))
            {
                int count=map.get(nums[i]);//先获得当前nums[i]的出现次数
                map.put(nums[i],count+1);//把当前nums[i]的出现次数加1
                if(count+1>nums.length/2)//计数超过一半,返回当前 nums[i]
                {
                    return nums[i];      
                }
            }
            else
            {
                map.put(nums[i],1);//第一次出现,存入nums[i],出现次数记为1
            }
           
        }
        return -1;
    }
}

参考大佬的Map解法

   class Solution{
    public int majorityElement(int[] nums) {
        Map<Integer, Integer> counts = new HashMap<>();
        int length = nums.length;
        for (int i = 0; i < length; i++) {
            int count = counts.getOrDefault(nums[i], 0) + 1;
            //如果某个数字出现的个数已经超过数组的一半,自己返回
            if (count > length / 2)
                return nums[i];
            counts.put(nums[i], count);
        }
        return -1;
    }
  }

在这里插入图片描述

3:位运算

在java中int类型是32位的,我们只需要判断所有数字在某一位1的个数大于数组长度的一半,那么这个众数在这个位置肯定就是1,我们需要遍历32次,确定这个众数每个位置是0还是1即可。
作者:sdwwld
链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/solution/pai-xu-mapwei-yun-suan-mo-er-tou-piao-fa-l47t/

   class Solution{
        public int majorityElement(int[] nums) {
        int major = 0;
        int length = nums.length;
        //在java中int类型是32位,我们遍历每一位
        for (int i = 0, mask = 1; i < 32; i++, mask <<= 1) {
            //bitCounts表示所有数字在当前位置1的个数。比如当i=0
            //的时候,我们可以认为他表示的是所有数字在二进制位
            //中最右边1的总数。
            int bitCounts = 0;
            for (int j = 0; j < length; j++) {
                //判断数字nums[j]的第i(i从0开始)个位置是否为1,
                //如果是1,bitCounts就加1
                if ((nums[j] & mask) != 0)
                    bitCounts++;
                //如果bitCounts大于数组的一半,那么这个众数在
                //这个位置肯定是1,然后通过 major |= mask运算
                //把这个位置变为1,后面不需要再判断了,直接终止
                //内层循环
                if (bitCounts > length / 2) {
                    major |= mask;
                    break;
                }
            }
        }
        return major;
    }
  }

在这里插入图片描述

4:随机数法

这个程序不能处理没有超过数组长度一半的数字的情况,需要改进,可以把while循环次数改一下嘛,改成数组长度的3倍?就不会因为找不到而死循环了

class Solution {
    int majorityElement(int[] nums) {    
//每一轮随机选择一个数字,统计出现次数,因为目标出现频率大于二分之一,所以效率较高
        Random r = new Random();
     
        while(true)
        {
            int candidate = nums[r.nextInt(nums.length)];
            int  count = 0;   
            for(int i=0;i<nums.length;i++)
            {
            if(nums[i] == candidate)     
                ++count;
            }
            if(count > nums.length/2)   
                
            return candidate;               
        }     
    }
}

5:摩尔投票法

参考:剑指 Offer 39. 数组中出现次数超过一半的数字(摩尔投票法,清晰图解)
摩尔投票法,投我++,不投–,超过一半以上的人投我,那我稳赢哇

核心理念为 票数正负抵消 。此方法时间和空间复杂度分别为 O(N)和 O(1) ,为本题的最佳解法。

我们思考一下,有可能两个都不是众数的数字被消除掉了,因为其中一个被当成了临时众数,比如[1,2,3,3,3],这样更加增加了众数的存活机会
还有可能就是一个众数消除一个非众数。比如[2,1,3,1,4,1,5,1,1,1,1,1,1];
而且最后总会出现一个x,它之后票数和不会再为0,使得票数和大于0;

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int majorityElement(int[] nums) {
        int x = 0, votes = 0;
        for(int num : nums){
            if(votes == 0) x = num;
            votes += num == x ? 1 : -1;
        }
        return x;
    }
}

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int majorityElement(int[] nums) {
        int x = 0, votes = 0, count = 0;
        for(int num : nums){
            if(votes == 0) x = num;
            votes += num == x ? 1 : -1;
        }
        // 验证 x 是否为众数
        for(int num : nums)
            if(num == x) count++;
        return count > nums.length / 2 ? x : 0; // 当无众数时返回 0
    }
}


作者:jyd
链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/solution/mian-shi-ti-39-shu-zu-zhong-chu-xian-ci-shu-chao-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

总结

HashMap的getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。
新的知识点:摩尔投票法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值