基本思想
歸并排序(MERGE-SORT)是利用歸并的思想實現的排序方法,該算法采用經典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題然后遞歸求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。
分而治之
1024555-20161218163120151-452283750.png
可以看到這種結構很像一棵完全二叉樹,本文的歸并排序我們采用遞歸去實現(也可采用迭代的方式去實現)。分階段可以理解為就是遞歸拆分子序列的過程,遞歸深度為log2n。
合并相鄰有序子序列
再來看看治階段,我們需要將兩個已經有序的子序列合并成一個有序序列,比如上圖中的最后一次合并,要將[4,5,7,8]和[1,2,3,6]兩個已經有序的子序列,合并為最終序列[1,2,3,4,5,6,7,8],來看下實現步驟。
1024555-20161218194508761-468169540.png
代碼實現
OC:
- (void)mergeSort:(NSMutableArray *)arr{
NSMutableArray *temp = [NSMutableArray arrayWithCapacity:arr.count];//在排序前,先建好一個長度等于原數組長度的臨時數組,避免遞歸中頻繁開辟空間
[self sort:arr Left:0 Right:(int)arr.count-1 Temp:temp];
}
- (void)sort:(NSMutableArray *)arr Left:(int)left Right:(int)right Temp:(NSMutableArray *)temp{
if(left<right){
int mid = (left+right)/2;
[self sort:arr Left:left Right:mid Temp:temp];//左邊歸并排序,使得左子序列有序
[self sort:arr Left:mid+1 Right:right Temp:temp]; //右邊歸并排序,使得右子序列有序
[self merge:arr Left:left Mid:mid Right:right Temp:temp];//將兩個有序子數組合并操作
}
}
- (void)merge:(NSMutableArray *)arr Left:(int)left Mid:(int)mid Right:(int)right Temp:(NSMutableArray *)temp{
int i = left;//左序列指針
int j = mid+1;//右序列指針
int t = 0;//臨時數組指針
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t++] = arr[i++];
}else {
temp[t++] = arr[j++];
}
}
while(i<=mid){//將左邊剩余元素填充進temp中
temp[t++] = arr[i++];
}
while(j<=right){//將右序列剩余元素填充進temp中
temp[t++] = arr[j++];
}
t = 0;
//將temp中的元素全部拷貝到原數組中
while(left <= right){
arr[left++] = temp[t++];
}
}
swift:
func mergeSort(arr:inout Array<Int>){
var temp = [Int](repeating: 0, count: arr.count)
self.sort(arr: &arr, left: 0, right:(arr.count-1), temp: &temp)
}
func sort(arr:inout [Int],left:Int,right:Int,temp:inout [Int]){
if left<right {
let mid:Int = (left + right)/2;
sort(arr: &arr, left: left, right: mid, temp: &temp)
sort(arr: &arr, left: mid+1, right: right, temp: &temp)
merge(arr: &arr, left:left, mid: mid, right: right, temp: &temp)
}
}
func merge(arr:inout [Int],left:Int,mid:Int,right:Int,temp:inout [Int]) {
var i = left
var j = mid + 1
var t = 0
while i<=mid && j<=right{
if(arr[i]<=arr[j]){
temp[t] = arr[i];
t = t+1
i = i+1
}else {
temp[t] = arr[j];
t = t+1
j = j+1
}
}
while i<=mid {
temp[t] = arr[i];
t = t+1
i = i+1
}
while j<=right {
temp[t] = arr[j];
t = t+1
j = j+1
}
t = 0;
var left = left
while left <= right{
arr[left] = temp[t];
left=left+1
t=t+1
}
}
最后
歸并排序是穩定排序,它也是一種十分高效的排序,能利用完全二叉樹特性的排序一般性能都不會太差。從上文的圖中可看出,每次合并操作的平均時間復雜度為O(n),而完全二叉樹的深度為|log2n|。總的平均時間復雜度為O(nlogn)。而且,歸并排序的最好,最壞,平均時間復雜度均為O(nlogn)。