由於最近本人要參加面試,複習了一下本科學習過的一些重要的基礎算法,在此總結歸納知識點,內容主要參考自百度百科並做了一些相應的修改。
歸併排序
1)算法思想:歸併排序是建立在歸併操作上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併。
歸併過程爲:比較a[i]和a[j]的大小,若a[i]≤a[j],則將第一個有序表中的元素a[i]複製到r[k]中,並令i和k分別加上1;否則將第二個有序表中的元素a[j]複製到r[k]中,並令j和k分別加上1,如此循環下去,直到其中一個有序表取完,然後再將另一個有序表中剩餘的元素複製到r中從下標k到下標t的單元。歸併排序的算法我們通常用遞歸實現,先把待排序區間[s,t]以中點二分,接着把左邊子區間排序,再把右邊子區間排序,最後把左區間和右區間用一次歸併操作合併成有序的區間[s,t]。
2)算法介紹:歸併操作(merge),也叫歸併算法,指的是將兩個順序序列合併成一個順序序列的方法。
如 設有數列{6,202,100,301,38,8,1}
初始狀態:6,202,100,301,38,8,1
第一次歸併後:{6,202},{100,301},{8,38},{1},比較次數:3;
第二次歸併後:{6,100,202,301},{1,8,38},比較次數:4;
第三次歸併後:{1,6,8,38,100,202,301},比較次數:4;
總的比較次數爲:3+4+4=11,;
逆序數爲14;
歸併操作的工作原理如下:
第一步:申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
第二步:設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
第三步:比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
重複步驟3直到某一指針超出序列尾
將另一序列剩下的所有元素直接複製到合併序列尾
3)用途:
速度僅次於快速排序,爲穩定排序算法,一般用於對總體無序,但是各子項相對有序的數列。
4)代碼實現:
public classMergeSort {
/**
* 歸併排序
* 簡介:將兩個(或兩個以上)有序表合併成一個新的有序表 即把待排序序列分爲若干個子序列,每個子序列是有序的。然後再把有序子序列合併爲整體有序序列
* 時間複雜度爲O(nlogn)
* 穩定排序方式
* @param nums 待排序數組
* @return 輸出有序數組
*/
public static int[] sort(int[] nums,int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左邊
sort(nums, low,mid);
// 右邊
sort(nums, mid + 1,high);
// 左右歸併
merge(nums, low,mid, high);
}
return nums;
}
public static void merge(int[] nums,int low, int mid, int high) {
int[] temp = new int[high -low + 1];
int i = low;// 左指針
int j = mid + 1;// 右指針
int k = 0;
// 把較小的數先移到新數組中
while (i <= mid &&j <= high) {
if (nums[i] <nums[j]) {
temp[k++] =nums[i++];
} else {
temp[k++] =nums[j++];
}
}
// 把左邊剩餘的數移入數組
while (i <= mid) {
temp[k++] =nums[i++];
}
// 把右邊邊剩餘的數移入數組
while (j <= high) {
temp[k++] =nums[j++];
}
// 把新數組中的數覆蓋nums數組
for (int k2 = 0; k2 <temp.length; k2++) {
nums[k2 + low] =temp[k2];
}
}
}
5)複雜度分析
歸併排序的最好、最壞和平均時間複雜度都是O(nlogn),而空間複雜度是O(n),歸併排序算法比較佔用內存,但卻是效率高且穩定的排序算法。(若從空間複雜度來考慮:首選堆排序,其次是快速排序,最後是歸併排序。若從穩定性來考慮,應選取歸併排序,因爲堆排序和快速排序都是不穩定的。若從平均情況下的排序速度考慮,應該選擇快速排序。
)
可以說合並排序是比較複雜的排序,特別是對於不瞭解分治法基本思想的同學來說可能難以理解。總時間=分解時間+解決問題時間+合併時間。分解時間就是把一個待排序序列分解成兩序列,時間爲一常數,時間複雜度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).