《劍指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基本用法及遍歷方式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章