1. 程式人生 > >劍指offer:把陣列排成最小的數(java)

劍指offer:把陣列排成最小的數(java)

題目:輸入一個正整數陣列,把數組裡面所有的數字拼接排成一個數,列印能拼接出的所有數字中的一個。例如輸入陣列{3,32,321},則打印出這3個數字能排成的最小數字321323.

    這個題目最直接的做法應該是先求出這個陣列中的所有數字的全排列,然後把每個排列拼接起來,最後求出排列起來的數字的最小值。求陣列的排列和麵試題28非常相似。根據排列組合的只是,n個數字總共有n!排列,我們再來看一下更快的演算法。

    這道題其實希望我們能夠找到一個排序規則,陣列根據這個規則排序之後能排成一個最小的數字。要確定排序的規則,就要比較兩個數字,也就是給出兩個數字m和n,我們需要確定一個規則判斷m和n哪個應該排在前面,而不是僅僅比較這兩個數字的值哪個更大。

    根據題目的要求,兩個數字m和n能拼接稱數字mn和nm。如果mn<nm,那麼我們應該打印出mn,也就是m應該拍在N的前面,那麼要保持mn<nm那麼我們定義m(isSmall)n,先將陣列中的整數轉換成字串,定義isSmall規則:即m的第一位<n的第一位,如果相等,則比較m的下一位<n的下一位,如果mn=nm,m等於n。

public void printMin(int[] arr){  
        int[] clone = arr.clone();  
        qsort(clone,0,clone.length-1);  
        for(int i : clone)  
            System.out.print(i);  
    }  
    //規則+快排  
    public void qsort(int[] arr,int left,int right){  
        if(left < right){  
            int main_number = arr[right];  
            int small_cur = left;  
            for(int j = left;j<right;j++){  
                if(isSmall(String.valueOf(arr[j]),String.valueOf(main_number))){  
                    int temp = arr[j];  
                    arr[j] = arr[small_cur];  
                    arr[small_cur] = temp;  
                    small_cur++;  
                }  
            }  
            arr[right]= arr[small_cur];  
            arr[small_cur] = main_number;  
            qsort(arr,0,small_cur-1);  
            qsort(arr,small_cur+1,right);  
        }  
    }  
    public boolean isSmall(String m,String n){  
        String left = m+n;  
        String right = n+m;  
        boolean result = false;  
        for(int i = 0;i<left.length();i++){  
            if(left.charAt(i)<right.charAt(i))  
                return true;  
            else  
                if(left.charAt(i)>right.charAt(i))  
                    return false;  
        }  
        return result;  
    }  

    證明規則的有效性:自反、對稱、傳遞

    顯然aa=aa,滿足自反性;

    若a(isSmall)b,則ab<ba,所以ba>ab,因此b(!isSmall)a,滿足對稱性;

    若a(isSmall)b,則ab<ba.假設a和b用十進位制表示有l位和m位,於是ab<ba->a/(10^l-1)<b/(10^m-1)

    若b(isSmall)c,則bc<cb.假設c用十進位制表示有n位,那麼b/(10^m-1)<c/(10^n-1),由a(isSmall)b推出a/(10^l-1)<b/(10^m-1),因此a/(10^n-1)<c/(10^l-1),因此a(isSmall)c,滿足傳遞性。


  接下來證明n為數按照前面的排序規則排序之後,表示A1A2...An,仍為最小。我們使用反證法,假設這樣拼接的數字並不是最小的,即存在兩個x和y(0<x<y<n),交換第x個數和第y個數後,A1A2...Ay...Ax...An<A1A2...Ax...Ay...An。由於A1A2...Ay...Ax...An是按照規則排序好的,所以有Ax(isSmall)Ax+1(isSmall)Ay-1(isSmall)Ay.我們一直交換Ay和前面的數字交換,直到和Ax交換為止,於是就有A1A2...Ax...Ay-1...Ay...An<A1A2...AyAx...Ay-1...An.同理我們一直拿Ax交換它後面的數字,直到和Ay-1交換為止。於是A1A2...AyAx...Ay-1...An<A1A2...AyAx+1...Ay-1Ax...An.所以A1A2...Ax...Ay...An<A1A2...Ay...Ax...An,與假設矛盾。因此假設不成立,我們的演算法正確。