我們不得不承認一個事實,java學習過程中如果我們掌握了各種編程手段和工具,確實可以做一些開發,這就是一些培訓機構敢告訴你幾個月就能掌握一門語言的原因。但是隨着時間的發展,我們總會感覺,這一類人如果不提升自己,最后也只會是一個碼農。技術會日新月異,隨時在發展更新換代,但是這幾十年,有誰說過算法會過時,如果我們說java語言的發動機是各種開發手段和技術,那么我們可以毫不客氣的說算法會是他的靈魂。一個程序員的提升和拔高一定是萬丈高樓平地起,那么我希望這個地基一定是數據結構和算法,掌握這些原理以后其實在以后的學習過程當中我們會異常的輕松。
首先,我們需要掌握幾種最基本的排序方式,比如簡單排序,插入排序,快速排序,堆排序,冒泡排序,選擇排序,希爾排序,歸並排序。每一種排序方式都有自己的特點,數據量的大小或者說對時間復雜度,空間復雜度的要求都可以選擇合適的排序方式進行排序。就我個人而言,因為在我學習的過程中對歸並排序情有獨鍾,所以今天我們在這里介紹一下歸並排序。我們這將演示歸並排序的java實現代碼,以及歸並的特點,它的時間復雜度和空間等等的問題計算。
一:歸並排序的特點:
歸並排序的特點第一點就是它是一個比較有代表性的分而治之思想的排序方式,其中滲透着遞歸的思想,當我們面對一大堆復雜的復雜的數據無從下手時,或許分而治之就是我們的首選,這就確定了歸 並排序最好是在數據量比較大,對空間復雜度要求不高,對時間復雜具有一定要求的情況下使用。因為它在不斷的遞歸拆分數據的過程中會占用棧的空間。說到這里可能有點迷糊,那我們直接上代碼 看如何實現歸並排序。
二:代碼實現:
第一步:我們稱之為歸並,那么必然涉及到了合並,如果有合並,那么必然會有相對應的拆分。在這一步我們對應的就是sort方法,讓一個完整的數組進行不斷的拆分,直到最后的數據單元是一個單獨 的數據位置,這時候在進行第二步,就是從小往上不斷地對分開的數據進行整合。
第二步:也就是我們看到的mergeArray()方法,它是通過一個臨時數組最為一個臨時的容器,將比較過后的有序數組存儲進去,然后再取出賦值給原始數據。這里需要提出幾個比較需要理解的地方,
第一點:就是我們在進行合並的時候是合並middle數據兩邊的數據集合,通過比較,讓小的進入臨時數組,角標加一,在進行比較,直到一個數據集合到底位置。
第二點:我們在進行比較以后必然會有一個數據集合會留下一些數據沒有進行插入,這時候我們就比較他們的開始和結束的角標,如果不等於那么就把這個數據集合里面的數據繼續通過while 循環加入我們的臨時數組中。
第三點:我們在最后的時候,把臨時數組中的數據加入原始數組,必須記住要加上start這個數值,因為我們都知道在合並的時候,我們左右是同時進行的,左邊的數據可能是從0開始,但是右 邊的數據就不見得了,所以我們要加上合並開始的位置start。
第四點:一定要注意一個事情,算法看懂很困難,但是我們或許需要實踐,在運算的邏輯處理不理解時,可以在草稿紙上進行過程分解,或者通過源代碼進行debag模式,分析數據的走向。
1 public static void sort(int arr[],int emp[],int start,int end){ 2 //對是否繼續拆分遞歸進行判斷 3 int middle = start + (end - start) / 2; 4 if(start < end){ 5 sort(arr,emp,start,middle);//讓左邊的數據進行拆分並實現有序 6 sort(arr,emp,middle + 1,ens);//讓右邊的數據進行拆分並實現有序 7 mergeArray(arr,emp,start,middle,end);//進行合並 8 } 9 } 10 public static void mergeArray(int arr[],int emp[],int start,int middle,int end){ 11 int i = start,j = middle; 12 int k = middle + 1,z = end; 13 int x = 0; 14 //判斷那個數據大小插入臨時數組 15 while(i <= j&& k <= z){ 16 if(arr[i] < arr[k]){ 17 emp[x++] = arr[i++]; 18 } else{ 19 emp[x++] = arr[k++]; 20 } 21 } 22 //當我們經拆分好的兩邊數據插入臨時數組以后肯定還有未完成的數據在原始數組中 23 //繼續進行數據清理,加入臨時數組 24 while(i <= j){ 25 emp[x++] = arr[i++]; 26 } 27 while(k <= z){ 28 emp[x++] = arr[k++]; 29 } 30 //所有數據進入臨時數組以后我們將數據導出,放入原始數組中,這些數據必然有序 31 for(i = 0;i < x;i++){ 32 arr[start + i] = emp[i]; 33 } 34 35 }
三:算法過程分析圖:
四:歸並的時間復雜度計算和分析: 對於這一塊來說我們或許可以試着去嘗試理解,對於各方面的綜合考慮來說對於初學者可能比較有難度。
可以說合並排序是比較復雜的排序,特別是對於不了解分治法基本思想的同學來說可能難以理解。總時間=分解時間+解決問題時間+合並時間。
分解時間:把一個待排序序列分解成兩序列,時間為一常數,時間復雜度o(1).
解決問題時間:兩個遞歸式,把一個規模為n的問題分成兩個規模分別為n/2的子問題,時間為2T(n/2).合並時間復雜度為o(n)。
總時間:T(n)=2T(n/2)+o(n).這個遞歸式可以用遞歸樹來解,其解是o(nlogn).此外在最壞、最佳、平均情況下歸並排序時間復雜度均為o(nlogn).從合並過程中可以看出合並排序穩定。
用遞歸樹的方法解遞歸式T(n)=2T(n/2)+o(n):假設解決最后的子問題用時為常數c,則對於n個待排序記錄來說整個問題的規模為cn。
從這個遞歸樹可以看出,第一層時間代價為cn,第二層時間代價為cn/2+cn/2=cn.....每一層代價都是cn,總共有logn+1層。所以總的時間代價為cn*(logn+1).時間復雜度是o(nlogn)