劍指Offer——數組中出現次數超過一半的數字——一題多解

看題目:算法

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

 

個人直接思路:函數

用map計數,簡單直接,遍歷一次數組,用hashmap記錄,key爲int值,value爲出現次數;spa

第二次再用map.entrySet找出有沒value大於數組長度通常的entry,有的話返回它的key。code

時間複雜度也是2n而已,這個方法時間複雜度是O(n)空間複雜度也是O(n)blog

 

代碼實現:排序

/*方法1
      蠻力,遍歷一次,用一個map來記錄
      第二次遍歷把出現次數大於length/2的那個值找出來
      */
    
    public int MoreThanHalfNum_Solution(int [] array) {
        int targetCount = array.length / 2;
        
        //key爲數組中的值,value爲出現的次數
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int temp : array) {
            if(map.get(temp) == null)map.put(temp, 1);
            else map.put(temp, map.get(temp) + 1);
        }
        Set<Entry<Integer,Integer>> entrySet = map.entrySet();
        for(Entry<Integer,Integer> entry : entrySet) {
            if(entry.getValue() > targetCount)return entry.getKey();
        }
        return 0;
    }
    

 

方法2:中位數get

若是數組是排好序的,就好解決了:
  若是排好序,而後又存在這樣的數字的話,那麼它的值確定和數組中間那個值是同樣的!!(好比:1,2,2,2,3;或2,2,2,3,4;或2,3,4,4,4等等)
 
因此咱們只須要排序後,一次遍歷,訪問每一個元素都和中間值比較,只要相等計數器就加一,遍歷完後若是計數器大於數組長度一半就返回那個中間元素就好。
 
排序最快是O(Nlogn),而後加上遍歷是O(n),空間複雜度是O(1)
 
代碼裏直接用的是Arrays的sort方法,它的實現沒記錯是O(Nlogn)的快排。
看代碼:
/*方法2
      排序後中位數法
      若是有個數字出現的次數大於數組長度的一半,那麼這個數組排序後,它的中位數必定是這個數字
    */
    
    public int MoreThanHalfNum_Solution(int [] array) {
        Arrays.sort(array);
        int count = 0, middleNum = array[array.length / 2];
        for(int temp : array) {
            if(temp == middleNum)count++;
        }
        if(count > array.length / 2)return middleNum;
        else return 0;
    }
    

 

 

方法3:——快排思路hash

方法2中,咱們排序是爲了找出中位數,那麼若是能夠更快地找出中位數就不用排序了。it

借鑑快速排序算法,其中的Partition()方法是一個最重要的方法,該方法返回一個index,可以保證index位置的數是已排序完成的,在index左邊的數都比index所在的數小,在index右邊的數都比index所在的數大。那麼本題就能夠利用這樣的思路來解。

經過Partition()返回index,若是index==mid,那麼 就代表找到了數組的中位數若是index<mid,代表中位數在[index+1,end]之間;若是index>mid,代表中位數在[start,index-1]之間。直到最後求得index==mid循環結束。
 
說白了就是找中位數這一步,咱們不選擇直接排序數組,而是用快排的Partition方法
 
該算法的時間複雜度爲O(n),空間複雜度爲O(1)
 
看代碼:
/*方法3
    利用快排的思想
    */
    
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length <= 0)return 0;
        
        int begin = 0, end = array.length - 1, middle = array.length / 2;
        int partition = partition(array, begin, end);
        
        while(partition != middle) {
            if(partition > middle) {//中位數在partition的左邊
                partition = partition(array, begin, partition - 1);
            } else {//中位數在partition右邊
                partition = partition(array, partition + 1, end);
            }
        }
        
        //找出中位數了,看這個中位數出現的次數是否符合要求
        int count = 0, middleKey = array[middle];
        for(int temp : array) {
            if(temp == middleKey)count++;
        }
        
        if(count > array.length / 2)return array[middle];
        else return 0;
    }
    
    //這個方法是以第一個元素爲基準,而後進行劃分,劃分後比基準元素小的數字都在它左邊,比它大的數字都在它右邊
    返回劃分後,這個元素的新index//
    private int partition(int[] a, int begin, int end) {
        int key = a[begin];
        
        int i = begin, j = end;
        while(i < j) {
            while(i < j && a[j] >= key)j--;
            while(i < j && a[i] <= key)i++;
            swap(a, i, j);
        }
        swap(a, begin, i);
        return i;
    }
    
    //交換數字函數,傳入數組還有要交換的兩個數字的index//
    private void swap(int[] a, int first, int second) {
        int temp = a[first];
        a[first] = a[second];
        a[second] = temp;
    }
    

 

方法4——陣地攻守思想

第一個數字做爲第一個士兵,守陣地;count = 1;
遇到相同元素,count++;
遇到不相同元素,即爲敵人,玉石俱焚,count--;當遇到count爲0的狀況,又以新的i值做爲守陣地的士兵,繼續下去,同時count更新爲1.
到最後還留在陣地上的士兵,有多是主元素。

再加一次循環,記錄這個士兵的個數看是否大於數組通常便可。
 
這個方法也是主要由於考慮到:題目中要找的數字出現的次數超過數組長度的一半,也就是說它出現的次數比其餘全部數字出現的次數的和還要多。,那麼確定最後留下來的是那個出現超過一半的。
 
該方法時間複雜度O(N),空間複雜度O(1)
 
 
實現的時候就維護兩個變量:一個是數組中的一個數字,一個是次數。

當咱們遍歷到下一個數字的時候,

若是下一個數字和當前咱們保存的數字相同,則次數加 1;

若是和當前咱們保存的數字不一樣,則次數減 1;

當次數減到 0 的時候,咱們將保存的數字改成當前遍歷所處的位置,並將次數更改成 1。

 

看代碼:

/*方法4
        陣地攻守思想,其實和牛客上面那個什麼「用戶分形葉」的思路同樣的,不一樣實現而已
    */
    public int MoreThanHalfNum_Solution(int [] array) {
        int count = 1, key = array[0];
        for(int temp : array) {
            if(temp == key)count++;
            else if(count > 0)count--;
            else {
                key = temp;
                count = 0;
            }
        }
        
        //判斷這個獲得的key是否是符合要求
        int count2 = 0;
        for(int temp : array) {
            if(temp == key)count2++;
        }
        if(count2 > array.length / 2)return key;
        else return 0;
    }
相關文章
相關標籤/搜索