Chapter3: 更好的查找与排序算法
3. 分治模式的完美诠释:归并排序
1. 基本思想
对于一次递归的调用来说
-
划分
- 取中间坐标,将原数组划分为两部分
- 分别递归对左右数组进行排序,该层调用的结果为左边数组有序,右边数组有序,但是左边数组的元素不一定比右边数组的小,如 [1,3,4,6,2,4,6,8]
-
合并
- 比如对于数组
arr=[1,5,3,7,2,6,8,4]
进行一次归并排序调用(包括划分和左右数组排序)后,得到左数组arr1=[1,3,5,7]
,右数组arr2=[2,4,6,8]
- 初始化: 让
left
指针指向做数组第一个元素,right
数组指向右数组第一个元素,设置一个辅助数组tmpArr=int [8]
; - 让
arr1[left]
与arr2[right]
比较,arr1[left]=1
比arr2[right]=2
小,将arr1[left]
存入tmpArr[0]
;arr1[left]
指针下移1位,即left++
, 此时arr1[left]=3
比arr2[right]=2
大,则将arr2[right]
存入tmpArr[1]
;right
++...循环直至其中一个数组的元素全部存入临时数组,另一个数组剩下的元素直接赋值到临时数组后面
- 比如对于数组
2. 代码实现
#include<iostream>
#include<cstdlib>
using namespace std;
void mergeSort(int* arr,int begin,int end);
void merge(int* arr,int begin,int mid,int end);
int main(){
int arr[6]={1,3,2,5,4,7};
int arrLen=sizeof(arr)/sizeof(arr[0]);
mergeSort(arr,0,arrLen-1);
for(int i=0;i<arrLen;i++){
printf("%d ",arr[i]);
}
return 0;
}
void mergeSort(int* arr,int begin,int end){
if(begin<end){//注意这个出口条件,不然会陷入死循环
int mid=begin+((end-begin)>>1);
mergeSort(arr,begin,mid);//递归调用起到划分作用
mergeSort(arr,mid+1,end);
merge(arr,begin,mid,end);
}
}
//合并函数
void merge(int* arr,int begin,int mid,int end){
int len=end-begin+1;//当前划分状态下的数组长度
int tmpArr[len];//辅助数组
int leftP=begin;//左指针
int rightP=mid+1;//右指针
int i=0;//辅助数组当前存取的下标
//printf("initial. len=%d\n",len);
while((leftP<=mid)&&(rightP<=end)){
if(arr[leftP]<=arr[rightP]){
tmpArr[i]=arr[leftP];
i++;
leftP++;
//或直接写为 tmpArr[i++]=arr[leftP++];
}
else{
tmpArr[i]=arr[rightP];
i++;
rightP++;
}
}
while(leftP<=mid){//如果左数组有剩余元素
tmpArr[i]=arr[leftP];
i++;
leftP++;
}
while(rightP<=end){//如果右数组有剩余元素
tmpArr[i]=arr[rightP];
i++;
rightP++;
}
/*将排序好的辅助数组复制到原数组中,
因为递归要用到上一层排好序的原数组,并所以这一步必不可少*/
i=0;
while(begin<=end){
arr[begin++]=tmpArr[i++];
}
//注意每次递归数组的开始位置不一样,为begin,不一定是0,所以不能用下面的赋值方法
// for(i=0;i<len;i++){
// arr[i]=tmpArr[i];
// }
}
3. 快速排序和归并排序的比较
快速排序关键在于划分,归并排序关键在于合并
快速排序划分的左右两个数组左边数组的元素一定小于右边数组的元素,但是在左数组或右数组内元素不一定有序
归并排序划分的左右两个数组左边的数组的元素不一定小于右边的元素,但是在左右数组内的元素分别是有序的
快速排序的思想
-
分解
数组
A[p..r]
被划分为两个子数组A[p...q-1]
和A[q+1,r]
, 使得A[q]
为大小居中的数,左侧A[p..q-1]
中的每个元素都<=
它, 而右侧A[q+1,r]
中的每个元素都>=
它。其中计算下标q
也是划分过程的一部分。 -
解决
通过递归调用快速排序,分别对子数组
A[p...q-1]
和 A[q...r] 进行排序 -
合并
不存在合并的问题
4. 参考资料
[1] 图解排序算法(四)之归并排序