169. 多数元素

文章介绍了三种在给定整数数组中找到多数元素的方法:哈希算法(O(n)时间复杂度),利用排序查找中间值(O(nlogn)时间复杂度),以及摩尔投票法(O(n)时间复杂度)。详细讲解了每种方法的思路和代码实现。
摘要由CSDN通过智能技术生成

题目:多数元素

链接:link

说明

这样的数有且只有一个,如果数组中有两个或更多的元素出现次数都超过数组长度的一半,那么这些元素的出现次数总和就会超过数组长度,这是不可能的

解题方法

方法一:哈希算法
思路:
  • 哈希表可以用来统计每个元素出现的次数,然后找到出现次数超过数组长度一半的元素。
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
代码
 public static int majorityElement(int[] nums) {  
        Map<Integer, Integer> countMap = new HashMap<>();  
        for (int num : nums) {  
            countMap.put(num, countMap.getOrDefault(num, 0) + 1);  
            if (countMap.get(num) > nums.length / 2) {  
            //如果有则立即返回
                return num;  
            }  
        }  
        // 如果没有多数元素,抛出异常或返回默认值  
        throw new IllegalStateException("No majority element found");  
    }  
方法二:排序
思路
  • 如果数组是有序的,那么多数元素一定位于数组的中间位置。可以先对数组进行排序,然后返回中间位置的元素。
  • 时间复杂度 O ( n l o g ⁡ n ) O(nlog⁡n) O(nlogn),空间复杂度 O ( l o g ⁡ n ) O(log⁡n) O(logn)
代码
 public static int majorityElement(int[] nums) {  
        Arrays.sort(nums);  
        return nums[nums.length / 2];  
    }  
方法三:摩尔投票法
思路
  • 由于多数元素的出现次数超过一半,那么在遍历数组的过程中,我们总是可以保留一个候选的多数元素,并统计它的出现次数。当遇到一个与当前候选多数元素不同的元素时,就将它的投票次数减1,如果相同,则加1;如果出现次数减到0,就更换候选元素。由于多数元素的出现次数超过一半,最终留下的候选元素一定是多数元素。
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
代码
    public int majorityElement(int[] nums) {
      int count = 0;
      int candidate = 0;
      for(int num : nums) {
        if(count == 0) {
            candidate = num;
        } 
        count += (candidate == num) ? 1 : -1;
      }
      return candidate;
    }

拓展:229. 多数元素 II

链接:link
思路
  • 初始化两个候选变量 candidate1 和 candidate2,以及两个计数器 count1 和 count2,都设为 0。遍历数组中的每个元素:
        如果当前元素等于 candidate1,则 count1 加 1。如果当前元素等于 candidate2,则 count2 加 1。
        如果当前元素既不等于 candidate1 也不等于 candidate2,如果 count1 为 0,则将当前元素设为 candidate1,并将 count1 设为 1。否则,如果 count2 为 0,则将当前元素设为 candidate2,并将 count2 设为 1。
        如果 count1 和 count2 都不为 0,则两个计数器都减 1。
  • 遍历完数组后,candidate1 和 candidate2 可能不是真正的答案,因为它们的计数器可能已经被减到了 0。因此,我们需要再次遍历数组,统计 candidate1 和 candidate2 的真实出现次数,看它们是否超过 ⌊ n/3 ⌋。
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
  • 总结:如果至多选一个代表,那他的票数至少要超过一半(⌊ 1/2 ⌋)的票数;如果至多选两个代表,那他们的票数至少要超过 ⌊ 1/3 ⌋ 的票数;如果至多选m个代表,那他们的票数至少要超过 ⌊ 1/(m+1) ⌋ 的票数。
class Solution {
    public List<Integer> majorityElement(int[] nums) {
        //结果
        List<Integer> result = new ArrayList<>();
        //初始化两个候选人和各自的投票数
        int can1 = nums[0], count1 = 0;
        int can2 = nums[0], count2 = 0;

        //配对,抵消处理阶段
        for (int num : nums) {
            //投票
            if(can1 == num) {
                count1++;
                continue;   //当前候选人获得投票,后面都不需要执行
            }
            if(can2 == num) {
                count2++;
                continue;   //同上
            }

            //当前面两个候选人都未获得投票时,进行配对替换,看哪个候选人将要被替代
            if (count1 == 0) {
                can1 = num;
                count1++;
                continue;  
            }
            if (count2 == 0) {
                can2 = num;
                count2++;
                continue;
            }

            //如果都未被替代,则各自抵消票数
            count1--;
            count2--;

        }

        // 计数阶段
        // 找到了两个候选人之后,需要确定票数是否满足大于 N/3
        count1 = 0;
        count2 = 0;
        for (int num : nums) {
            if (can1 == num) count1++;
            else if (can2 == num) count2++;
        }

        if (count1 > nums.length / 3) result.add(can1);
        if (count2 > nums.length / 3) result.add(can2);

        return result;     
       
    }
}
  • 27
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值