對比快速排序來理解歸併排序。
有時經常講歸併排序和快速排序記混亂,因爲兩者都用到了分治法。其實兩者的不同之處非常明顯。
快速排序:“先治後分”,
void quickly_sort(int arr[], int low, int high) {
if (low < high) {
// 先治後分
int pivot = _qucikly_sort(arr, low, high); //治
quickly_sort(arr, low, pivot-1); //分
quickly_sort(arr, pivot + 1, high); //分
}
}
歸併排序:“先分後治”。
void merge_sort_up2down(int a[], int start, int end) {
if(a==NULL || start >= end)
return ;
// 先分後治
int mid = (end + start)/2;
merge_sort_up2down(a, start, mid); // 分
merge_sort_up2down(a, mid+1, end); // 分
merge(a, start, mid, end); // 治
}
“分”的過程用遞歸實現起來分簡單,主要是理解兩者的“治”。
快速排序的根本處理是:如何在選定一個元素後,將這個元素放在合適的位置,使得元素左邊都是小於這個元素的,右邊都是大於這個元素的(挖坑填坑法):int _qucikly_sort(int arr[], int low, int high) {
int pivot = arr[low]; //挖第一個坑,把“蘿蔔”放進籃子
while(low<high) { //開始循環挖坑填坑
//如果low等於high,說明找坑的過程中,找到了上一次挖的坑,因此可以停止了
while(low<high && arr[high]>=pivot) //從右邊往左邊找填坑的元素(比pivot小的元素)。 2,4,6的過程
--high;
arr[low] = arr[high]; //將找到之後,挖出來,填到左邊的坑裏
while(low<high && arr[low]<=pivot) //從左邊往右邊找填坑的(比pivot大的元素)。3,5的過程
++low;
arr[high] = arr[low];//找到之後,填到右邊的坑裏
}
arr[low] = pivot; //籃子裏的“蘿蔔”填到坑裏
return low;//返回這個坑的位置,作爲分而治之的中軸
}
歸併排序的根本處理是:如何將兩個有序的數組,合併成一個有序數組(申請一個數組,大小是兩個有序數組(原數組的兩個子數組)的大小之和,遍歷比較兩個數組的元素,按大小放進新數組,將新數組替換原數組的子數組即可):
void merge(int a[], int start, int mid, int end) {
int *tmp = (int *)malloc((end-start+1)*sizeof(int)); // tmp是彙總2個有序區的臨時區域
int i = start; // 第1個有序區的索引
int j = mid + 1; // 第2個有序區的索引
int k = 0; // 臨時區域的索引
while(i <= mid && j <= end) {
if (a[i] <= a[j])
tmp[k++] = a[i++];
else
tmp[k++] = a[j++];
}
while(i <= mid)
tmp[k++] = a[i++];
while(j <= end)
tmp[k++] = a[j++];
// 將排序後的元素,全部都整合到數組a中。
for (i = 0; i < k; i++)
a[start + i] = tmp[i];
free(tmp);
}
這樣,都過簡單的代碼對比,就可以很簡單的分清楚兩者的關係與不同。