劍指offer之打印超過數組一半的數字

問題描述

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度為9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。

解題思路

該問題有很多種解法,其中包括了使用HashMap、排序與候選法進行解題。
這裡主要是講解有關於使用候選法來解決這道算法問題。

候選法

一開始,我在看到這個問題的第一反映是通過哈希表來解決這個問題。當我使用了HashMap方法解決了這個問題之後,我覺得這道題應該不是考察使用哈希表來解決的。

所以,我就查看了題解,在官方的題解方法中,看到了候選的方法,具體的候選法的思路如下所示:

  1. 首先,設置候選者cnt=-1,並且設置候選數為count = 0
  2. 然後遍歷整個數組。判斷如果候選數0,則將候選者設置為當前數字;如果候選數大於0,則判斷當前值是否與候選數相同,如果相同,則將候選數count++; 如果不相同,則將count–;
  3. 遍歷完數組後,剩下的cnt為臨時眾數。需要再重新遍歷一次數組,判斷cnt的出現次數是否查過數組長度的一般。

算法的實現過程

使用HashMap方法

因為使用該方法過於簡單,所以我就不再講解這個解題的思路。

public int MoreThanHalfNum_Solution(int [] array) {
        if(array == null || array.length == 0) return 0;
        int len = array.length;
        int mid = len / 2;
        // 建立HashMap存儲該數組中的位數,只需要遍歷一次即可
        Map<Integer, Integer> map = new HashMap<>();
        for(int i=0; i<len; i++){
            int m = array[i];
                map.put(m, map.getOrDefault(m, 0)+1);
        }
        for(int key: map.keySet()){
            if(map.get(key) > mid) return key;
        }
        
        return 0;
    }

使用候選法求解

//候選法,如果一個數為眾數,那麼這個數一定比其他的數存在的更多,可以抵消掉其他的數
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array == null || array.length ==0 ) return 0;
        int len = array.length;
        int mid = len/2;
        int cnt = -1;
        int count = 0;
        
        //首先遍曆數組,找出眾數
        for(int i=0; i<len; i++){
            if(count == 0){
                cnt = array[i];
                count ++;
            }else{
                if(cnt == array[i]){
                    count ++;
                }else{
                    count --;
                }
            }
        }
        
        //獲得到眾數,然後判斷眾數的個數是否大於數組的一半
        count = 0;
        for(int i=0; i<len; i++){
            if(cnt == array[i]) count ++;
        }
        
        if(count > mid) return cnt;
        else return 0;
        
    }