《剑指offer》 面试题39. 数组中出现次数超过一半的数字

题目描述

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

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

限制:
1 <= 数组长度 <= 50000

自己的想法(比较low)

因为用Java写的,所以借助的Map容器,正好也巩固了下HashMap常用方法和遍历方式。看了官方解法后…发现无论从空间还是时间是上说,才发现又更简单的方法

class Solution {
    public int majorityElement(int[] nums) {
        if(nums.length == 1) return nums[0];
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for(int i = 0; i < nums.length; i++){
            if(map.containsKey(nums[i])){
                map.put(nums[i], map.get(nums[i])+1);
            }else{
                map.put(nums[i], 1);
            }
        }
        int res = 0;
        for(Map.Entry<Integer, Integer> entry: map.entrySet()){
            if(entry.getValue() > nums.length/2){
                res =  entry.getKey();
                break;
            }
        }
        return res;
    }
}

官方解法(摩尔投票法)

很巧妙,使用投票法来解决
摩尔投票法:
票数和: 由于众数出现的次数超过数组长度的一半;若记 众数 的票数为 +1 ,非众数 的票数为 -1 ,则一定有所有数字的 票数和 >0 。
票数正负抵消: 设数组 nums 中的众数为 x ,数组长度为 n 。若 nums 的前 a 个数字的 票数和 = 0 ,则 数组后 (n−a) 个数字的 票数和一定仍>0 (即后 (n−a) 个数字的 众数仍为 x )
在这里插入图片描述

算法原理

为构建正负抵消,假设数组首个元素 n1为众数,遍历统计票数,当发生正负抵消时,剩余数组的众数一定不变 ,这是因为(设真正的众数为 x )

  • 当 n1=x 时: 抵消的所有数字中,有一半是众数 x 。
  • 当 n1!=x时: 抵消的所有数字中,少于或等于一半是众数 xx

利用此特性,每轮假设都可以 缩小剩余数组区间 。当遍历完成时,最后一轮假设的数字即为众数(由于众数超过一半,最后一轮的票数和必为正数)

class Solution {
    public int majorityElement(int[] nums) {
        int candidate = nums[0];
        int count = 1;
        for (int i = 1;i < nums.length;i ++) {
            if (nums[i] == candidate) {
                count++;
            }
            else {
                if (count == 0) {
                    candidate = nums[i];
                }
                else {
                    count--;
                }
            }
        }
        return candidate;
    }
}

收获到了:

  1. 摩尔投票法。票数和: 由于众数出现的次数超过数组长度的一半。
  2. HashMap基本用法及遍历方式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章