劍指Offer(java版):數組中出現次數超過一半的數字

題目:數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。因爲數字2在數組中出現5次,超過數組長度的一半,所以輸出2.java

解法一:基於Partition函數的O(n)算法:算法

  1. 通 過Partition()返回index,若是index==mid,那麼就代表找到了數組的中位數;若是index<mid,代表中位數在 [index+1,end]之間;若是index>mid,代表中位數在[start,index-1]之間。知道最後求得index==mid循 環結束。
  2. 根據求得的index,遍歷一遍數組,每當出現一個等於index所指向的數時time++,最後判斷time是否大於數組長度的一半,若是大於則代表index所指向的數就是所求的數,若是不是,則代表不存在一個數出現的次數超過數組長度的一半。

咱們的算法是受快速排序的算法的啓發。在隨機快速排序的算法中,咱們 先在數組中隨機的選擇一個數字,而後調數組中數字的順序,使得比選中的數字小數字排在它的左邊,比選中的數字大的數字都排在它的右邊。好比這個選中的數字 的下標恰好是n/2,那麼這個數字就是數組中的中位數。若是它的下標大於n/2,那麼中位數應該位於它的左邊,咱們能夠接着在它的左邊部分的數組中查找。 若是它的下標小於n/2,那麼中位數應該在它的右邊,咱們能夠接着在它的右邊部分中查找。這是一個典型的遞歸過程。數組

Java代碼實現過程以下:函數

package cglib;spa

 

public class DeleteNode{
     public int partition(int[] arr,int left,int right){  
            int result = arr[left];  //選中的數字
            System.out.println("arr[left="+left+"]"+arr[left]);
            System.out.println("arr[right="+right+"]"+arr[right]);
            if(left > right)  
                return -1;  
              
            while(left <right){  
                while(left <right && arr[right]>= result){  
                    right --;
                    System.out.println("right--="+right);
                }  
                System.out.println("交換前arr[right="+right+"]"+arr[right]);
                arr[left] = arr[right];
                System.out.println("交換後arr[left="+left+"]"+arr[left]);
                while(left <right && arr[left] <result){  
                    left++;
                    System.out.println("left++="+left);
                }
                System.out.println("交換前arr[left="+left+"]"+arr[left]);
                arr[right] = arr[left];
                System.out.println("交換後arr[right="+right+"]"+arr[right]);
            }  
            arr[left] = result;
            System.out.println("最後:arr[left="+left+"]"+arr[left]);
            System.out.println("最後返回left:"+left);
            return left;  
        }  
        public int moreThanHalfNum(int[] arr){  
            if(arr.length ==0)  
                return -1;  
              
            int length = arr.length;  
            int middle = length >>1;  
            int start = 0;  
            int end = length -1;  
            int index = partition(arr,start,end);
            System.out.println("中間下標index:"+index);
            while(index != middle){  
                if(index >middle){//選中的數字,數組下標大於中位數,則往左邊找
                    end = index - 1;  
                    System.out.println("進入if數組下標大於中位數,中間下標index:"+index);
                    index = partition(arr,start,end);
                    System.out.println("進入partition後數組下標大於中位數,中間下標index:"+index);
                }  
                else{ //選中的數字,數組下標小於中位數,則往右邊找
                    start = index + 1;
                    System.out.println("進入else數組下標小於中位數,中間下標index:"+index);
                    index = partition(arr,start,end);  
                    System.out.println("進入partition後數組下標小於中位數,中間下標index:"+index);
                }  
            }  
              
            int result = arr[middle];  
            if(!checkMoreThanHalf(arr,result)){  
                result = -1;  
            }  
            return result;  
        }  
        //驗證是否存在  
        public boolean checkMoreThanHalf(int[] arr,int number){  
            int times = 0;  
            for(int i = 0;i<arr.length;i++){  
                if(arr[i] == number)  
                    times ++;  
            }  
            boolean isMoreThanHalf = true;  
            if(times *2 <= arr.length){  
                isMoreThanHalf = false;  
            }  
            return isMoreThanHalf;  
        }  
        public static void main(String[] args){  
            int[] arr= {1,2,3,3,2,5,4,2,2,2,2};  
            DeleteNode test = new DeleteNode();  
            System.out.println(test.moreThanHalfNum(arr));  
        }
            }
            排序


輸出:遞歸

arr[left=0]1
arr[right=10]2
right--=9
right--=8
right--=7
right--=6
right--=5
right--=4
right--=3
right--=2
right--=1
right--=0
交換前arr[right=0]1
交換後arr[left=0]1
交換前arr[left=0]1
交換後arr[right=0]1
最後:arr[left=0]1
最後返回left:0
中間下標index:0
進入else數組下標小於中位數,中間下標index:0
arr[left=1]2
arr[right=10]2
right--=9
right--=8
right--=7
right--=6
right--=5
right--=4
right--=3
right--=2
right--=1
交換前arr[right=1]2
交換後arr[left=1]2
交換前arr[left=1]2
交換後arr[right=1]2
最後:arr[left=1]2
最後返回left:1
進入partition後數組下標小於中位數,中間下標index:1
進入else數組下標小於中位數,中間下標index:1
arr[left=2]3
arr[right=10]2
交換前arr[right=10]2
交換後arr[left=2]2
left++=3
交換前arr[left=3]3
交換後arr[right=10]3
right--=9
交換前arr[right=9]2
交換後arr[left=3]2
left++=4
left++=5
交換前arr[left=5]5
交換後arr[right=9]5
right--=8
交換前arr[right=8]2
交換後arr[left=5]2
left++=6
交換前arr[left=6]4
交換後arr[right=8]4
right--=7
交換前arr[right=7]2
交換後arr[left=6]2
left++=7
交換前arr[left=7]2
交換後arr[right=7]2
最後:arr[left=7]3
最後返回left:7
進入partition後數組下標小於中位數,中間下標index:7
進入if數組下標大於中位數,中間下標index:7
arr[left=2]2
arr[right=6]2
right--=5
right--=4
right--=3
right--=2
交換前arr[right=2]2
交換後arr[left=2]2
交換前arr[left=2]2
交換後arr[right=2]2
最後:arr[left=2]2
最後返回left:2
進入partition後數組下標大於中位數,中間下標index:2
進入else數組下標小於中位數,中間下標index:2
arr[left=3]2
arr[right=6]2
right--=5
right--=4
right--=3
交換前arr[right=3]2
交換後arr[left=3]2
交換前arr[left=3]2
交換後arr[right=3]2
最後:arr[left=3]2
最後返回left:3
進入partition後數組下標小於中位數,中間下標index:3
進入else數組下標小於中位數,中間下標index:3
arr[left=4]2
arr[right=6]2
right--=5
right--=4
交換前arr[right=4]2
交換後arr[left=4]2
交換前arr[left=4]2
交換後arr[right=4]2
最後:arr[left=4]2
最後返回left:4
進入partition後數組下標小於中位數,中間下標index:4
進入else數組下標小於中位數,中間下標index:4
arr[left=5]2
arr[right=6]2
right--=5
交換前arr[right=5]2
交換後arr[left=5]2
交換前arr[left=5]2
交換後arr[right=5]2
最後:arr[left=5]2
最後返回left:5
進入partition後數組下標小於中位數,中間下標index:5
2it


或者:
先將數組進行快排,若是數組中有一個數字出現的次數超過數組長度的一半,那麼快排後數組中間的數就必定是這個數字。遍歷快排後的數組,若是最中間的數的個數大於數組長度的一半,則輸出這個數,不然輸出0;io

 

package cglib;class

import java.util.Arrays;

public class DeleteNode{
    public int moreThanHalfNum(int [] array) {
        if(array.length == 0 || array == null){
            return 0;
        }
        Arrays.sort(array);
        int mid = array[array.length/2];
        int j = 0;
        for (int i : array){
             
            if (i == mid){
                j++;
            }
        }
        return j > array.length/2 ? mid : 0;
    }
        public static void main(String[] args){  
            int[] arr= {1,2,3,3,2,5,4,2,2,2,2};  
            DeleteNode test = new DeleteNode();  
            System.out.println(test.moreThanHalfNum(arr));  
        }
            }

 

輸出2

 

解法二:根據數組的特色找出O(n)的算法:

 

接下來咱們從另一個角度來解決這個問題。數組中有一個數字出現的次 數超過數組長度的一半,也就是說它出現的次數比其餘全部數字出現的次數的和還要多。所以咱們能夠遍歷數組的時候保存兩個值:一個是數組中的一個數字,一個 是次數。當咱們遍歷到下一個數字的時候,若是下一個數字和咱們以前保存的數字相同,則次數加1;若是下一個數字和咱們以前保存的數字不一樣,則次數減1.如 果次數爲0,咱們須要保存下一個數字,並把次數設爲1.因爲咱們要找的數字出現的次數比其餘全部數字出現的次數之和還要多,那麼要找的數字確定是最後一次 把次數設爲1時對應的數字。

 

package cglib;

 

public class DeleteNode{
    public int moreThanHalfNum2(int[] arr){  
        if(arr.length == 0)  
            return -1;  
        int result = arr[0];  
        int times = 1;  
        for(int i = 1;i<arr.length;i++){  
            if(times == 0){  
                result = arr[i];  
                times = 1;  
            }else if(arr[i] == result)  
                times++;  
            else  
                times--;  
        }  
        if(!checkMoreThanHalf(arr,result))  
            result = -1;  
        return result;  
    }  
        //驗證是否存在  
        public boolean checkMoreThanHalf(int[] arr,int number){  
            int times = 0;  
            for(int i = 0;i<arr.length;i++){  
                if(arr[i] == number)  
                    times ++;  
            }  
            boolean isMoreThanHalf = true;  
            if(times *2 <= arr.length){  
                isMoreThanHalf = false;  
            }  
            return isMoreThanHalf;  
        }  
        public static void main(String[] args){  
            int[] arr= {1,2,3,3,2,5,4,2,2,2,2};  
            DeleteNode test = new DeleteNode();  
            System.out.println(test.moreThanHalfNum2(arr));  
        }
            }
            


輸出

2

相關文章
相關標籤/搜索