1. 程式人生 > >排序17:相鄰兩數最大差值

排序17:相鄰兩數最大差值

題目:有一個整形陣列A,請設計一個複雜度為O(n)的演算法,算出排序後相鄰兩數的最大差值。給定一個int陣列A和A的大小n,請返回最大的差值。保證陣列元素多於1個。測試樣例:[1,2,5,4,6],5返回:2

思路:直接的思路,先排序,再遍歷陣列求出相鄰2個數的差,保留最大值即可,此時時間複雜度為O(nlogn)。本題採用的是桶排序的思想,對於n個數,設定n+1個桶,具體的,先遍歷陣列找出min和max,差值區間設定為d=(max-min)/n,於是區間是[min~min+d);[min+d~min+2d)……[min+(n-1)d~min+nd)這n區間,再單獨為最大值max設定一個桶,於是得到n+1個桶,已知陣列元素總共有n個,但是桶有n+1個,並且元素min必定在第一個桶中,max必定在n+1號桶中;顯然要把n個元素放入到n+1個桶中必定會產生空桶,已知陣列中的元素時整數,而雖然每個桶區間不一定是整數,例如[23/7,31/7),但是沒有關係,對於任意一個元素,它總會落入到某一個桶中,並且可以知道,不同桶之間元素的差值必定大於d,同一個桶中的元素的差值必定小於d,於是可知要求元素之間的最大差值只需要在不同的桶之間尋找,不用在同一個桶中尋找,並且題目要求是排序後相鄰2個元素的最大差值,於是應該尋找2個相鄰非空桶中前一個桶的最大值與後一個桶的最小值之間的差值,遍歷所有桶,比較得到所有差值中的最大值就是結果所要求的相鄰2數最大差值。注意理解:最終要對每2個非空的相鄰的桶求差值,而不能根據空桶的數目來確定2個相鄰桶的差值,因為桶的長度可能是小數,那麼間隔2個空桶和間隔3個空桶並不意味著後者具有更大的差值,因此應該對所有相鄰的非空桶根據前桶的最大值和後桶的最小值求出差值作為比較物件,因此在遍歷時對於每一個桶,要求出這個桶中元素的最大值maxOfBucket[i],最小值minOfBucket[i],並且保留上一個非空桶的maxOfLastBucket,於是差值著2個相鄰非空桶的差值就是result= minOfBucket[i]- maxOfLastBucket;然後將當前桶的maxOfBucket[i]最為下一個桶的maxOfLastBucket。逐步替換result,當遍歷完成時就可以得到最大差值。

具體的解決方式:

①已知應該設定的桶的數目是n+1,建立一個數組boolean[] hasNumber=new blloean[n+1];用來表示每個桶i中是否存在元素;如果沒有元素就是空桶,可以直接跳過。

②設定陣列maxOfBucket[],minOfBucket[]用來記錄每個桶中元素的最大值和最小值,設定變數maxOfLastBucket用來表示上一個非空桶的最大值。

③建立方法private int getBucketIndex(int number,intlength,int max,int min)根據一個數組的長度,最大值最小值,已知資料number,計算number這個數會被雜湊到的桶的下標。

④先將陣列中的所有元素雜湊到桶中,更新所在桶的hasNumber[i]、maxOfBucket[i]、minOfBucket[i]的值;

然後從i=0的桶開始遍歷桶,先找到第一個非空的桶,將其最大值作為下一個元素的maxOfLastBucket,然後往下遍歷,跳過空的桶,遇到非空的桶,就計算result= minOfBucket[i]- maxOfLastBucket,並且與已有的result比較,保留最大值,直到遍歷結束返回結果result即可。
import java.util.*;
//這道題目的思路很棒,巧妙而靈活:利用桶排序的思想雜湊陣列,求最大差值就是求2個相鄰非空桶的最大差值
public class Gap {
    public int maxGap(int[] A, int n) {
        //特殊輸入:如果只有一個元素,顯然最大差值為0
        if(A==null||A.length<2) return 0;
        
        //建立n+1個桶,hasNumber[]表示該桶是否有元素
        boolean[] hasNumber=new boolean[A.length+1];
        //每個桶中可以有多個數,記錄每個桶的最大值和最小值
        int[] maxOfBucket=new int[A.length+1];
        int[] minOfBucket=new int[A.length+1];
        //記錄上一個桶的最大值
        int maxOfLastBucket=0;
        
        //進行雜湊,把陣列中的元素散入桶,注意不要搞混桶的下標和元素的下標,這裡研究的都是桶的下標
        for(int i=0;i<A.length;i++){
            //求出元素A[i]落入的桶的下標bucketIndex
            int bucketIndex=this.getIndexOfBucket(A,i);
            
            //判斷桶中是否已經有元素
            if(hasNumber[bucketIndex]){
               //如果存在,則要比較A[i]與已有的maxOfBucket[i]和minOfBucket[i]
                maxOfBucket[bucketIndex]=A[i]>maxOfBucket[bucketIndex]? A[i]:maxOfBucket[bucketIndex];
                minOfBucket[bucketIndex]=A[i]<minOfBucket[bucketIndex]? A[i]:minOfBucket[bucketIndex];
            }else{
              //若不存在則將A[i]加入並作為maxOfBucket[i]和minOfBucket[i]
                maxOfBucket[bucketIndex]=A[i];
                minOfBucket[bucketIndex]=A[i];
              //將hasNumber[i]設為true表示桶中有元素,boolean[]陣列值預設為false
                hasNumber[bucketIndex]=true;
            }
        }
        
        //上面已經將所有元素雜湊到了n+1個桶中,開始遍歷桶求非空相鄰桶之間的最大差值
        //先找到第一個非空的桶,顯然第一個桶bucketIndex=0就是非空的(存放最小值),將其最大元素值作為maxOfLastBucket
        maxOfLastBucket=maxOfBucket[0];
        //result就是結果最大差值,初始值設為0,當全部元素相同時就取到0
        int result=0; 
        
        //從第二個桶開始遍歷,跳過空桶,共有n+1個桶,編號從0~n求出2個相鄰非空桶的最大差值
        for(int i=1;i<n+1;i++){
            //跳過空的桶,即不做任何處理
            if(hasNumber[i]){
                //不為空桶
                //比較是否替換最大差值
                result=(minOfBucket[i]-maxOfLastBucket)>result? (minOfBucket[i]-maxOfLastBucket):result;
                //將當前桶的最大值作為下一個桶的maxOfLastBucket
                maxOfLastBucket=maxOfBucket[i];
            }
        }
        return result;
    }
    
	//已知一個數組A,並且已知將其分成n+1個桶,給定A[i],求出應該落入的桶的下標
    private int getIndexOfBucket(int[] array,int i){
        //求出陣列array的最大、最小值
        int max=array[0];
        int min=array[0];
        for(int j=1;j<array.length;j++){
           max=array[j]>max? array[j]:max;
           min=array[j]<min? array[j]:min;
        }
        
        //這是一個邏輯,用於根據A[i]求出落入桶的下標
        int bucketIndex=(int)(((double)(array[i]-min))/(max-min)*array.length);
        return bucketIndex;
    }
}