1. 程式人生 > >【Java】 劍指offer(39) 陣列中出現次數超過一半的數字 《劍指Offer》Java實現合集 《劍指Offer》Java實現合集

【Java】 劍指offer(39) 陣列中出現次數超過一半的數字 《劍指Offer》Java實現合集 《劍指Offer》Java實現合集

本文參考自《劍指offer》一書,程式碼採用Java語言。

更多:《劍指Offer》Java實現合集  

題目 

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

思路

  思路一:數字次數超過一半,則說明:排序之後陣列中間的數字一定就是所求的數字。

  利用partition()函式獲得某一隨機數字,其餘數字按大小排在該數字的左右。若該數字下標剛好為n/2,則該數字即為所求數字;若小於n/2,則在右邊部分繼續查詢;反之,左邊部分查詢。

  思路二:數字次數超過一半,則說明:該數字出現的次數比其他數字之和還多

  遍歷陣列過程中儲存兩個值:一個是陣列中某一數字,另一個是次數。遍歷到下一個數字時,若與儲存數字相同,則次數加1,反之減1。若次數=0,則儲存下一個數字,次數重新設定為1。由於要找的數字出現的次數比其他數字之和還多,那麼要找的數字肯定是最後一次把次數設定為1的數字。

  也可以這樣理解(來源:牛客網 cm問前程):

  採用陣地攻守的思想:
  第一個數字作為第一個士兵,守陣地;count = 1;
  遇到相同元素,count++;
  遇到不相同元素,即為敵人,同歸於盡,count--;當遇到count為0的情況,又以新的i值作為守陣地的士兵,繼續下去,到最後還留在陣地上的士兵,有可能是主元素。


  再加一次迴圈,記錄這個士兵的個數看是否大於陣列一般即可。

 

測試算例 

  1.功能測試(存在或者不存在超過陣列長度一半的數字)

  2.特殊測試(null、1個數字)

Java程式碼

//題目:陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例
//如輸入一個長度為9的陣列{1, 2, 3, 2, 2, 2, 5, 4, 2}。由於數字2在陣列中
//出現了5次,超過陣列長度的一半,因此輸出2。

public class MoreThanHalfNumber {
    boolean isInputInvalid = true;
    
   //方法一:partition方法
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array==null ||array.length<=0)
            return 0;
        int low=0;
        int high=array.length-1;
        int index=partition(array,low,high);
        while(index!=array.length>>1){
            if(index<array.length>>1){
                low=index+1;
                index=partition(array,low,high);
            }else{
                high=index-1;
                index=partition(array,low,high);
            }
        }
        //判斷次數是否超過一半
        int num=array[index];
        int times=0;
        for(int i=0;i<array.length;i++){
            if(array[i]==num){
                times++;
            }
        }
        if(times*2>array.length){
            isInputInvalid=false;
            return num;
        }
        return 0;
    }
    
    private int partition(int[] array,int low ,int high){
        int pivotKey=array[low];
        while(low<high){
            while(low<high &&  array[high]>=pivotKey)
                high--;
            int temp=array[low];
            array[low]=array[high];
            array[high]=temp;
            while(low<high && array[low]<=pivotKey)
                low++;
            temp=array[low];
            array[low]=array[high];
            array[high]=temp;
        }
        return low;
    }
    
    //方法二
    public int MoreThanHalfNum_Solution2(int [] array) {
        if(array==null || array.length<=0)
            return 0;
        int num=array[0];
        int count=1;
        for(int i=1;i<array.length;i++){
            if(count==0)
                num=array[i];
            else if(array[i]==num)
                count++;
            else
                count--;
        }
        if(count>0){
            int times=0;
            for(int i=0;i<array.length;i++){
                if(array[i]==num){
                    times++;
                }
            }
            if(times*2>array.length){
                isInputInvalid=false;
                return num;
            }
        }
        return 0; 
    }
}

  

收穫

  1.length/2 用 length>>1 來代替,具有更高的效率;

  2.本題中,找到了所求數字,別忘記判斷該數字的次數是否超過一半,感覺很容易忘記進行判斷。

  3.題目所要求的返回值為int,所以如果陣列不滿足要求時,無法通過返回值來告知是否出錯,所以這道題設定了一個全域性變數來進行判斷。呼叫該方法時,需要記得對全域性變數進行檢查。

  4.方法一中,採用了partition()函式,該函式會改變修改的陣列,因此在面試的時候,需要和麵試官討論是否可以修改陣列。

  5.兩種方法的時間複雜度均為O(n)。

 

更多:《劍指Offer》Java實現合集