高级排序算法
1.归并排序
1.1 步骤
实现两个有序数组的合并
void merge(int arr[],int n,int mid);
拆分并合并数组
void merge_sort(int arr[],int n);
1.2 参考代码
void merge(int arr[],int n,int mid){
int temp[n];
memcpy(temp,arr,n*sizeof(int));
int p = 0,q = mid,k = 0;
while(p<mid && q<n) arr[k++] = temp[p]<temp[q]?temp[p++]:temp[q++];
if(p<mid) memcpy(arr+k,temp+p,(mid-p)*sizeof(int));
if(q<n) memcpy(arr+k,temp+q,(n-q)*sizeof(int));
}
void merge_sort(int arr[],int n){
if(n <= 1) return;
int mid = n/2;
merge_sort(arr,mid);
merge_sort(arr+mid,n-mid);
merge(arr,n,mid);
}
// [0,mid) [mid,n)
void merge(int* arr,int n,int mid){
int temp[n];
int p =0, // 前半部分下标
q=mid, // 后半部分下标
k=0; // 结果下标
while(p<mid && q<n){
if(arr[p]<arr[q]){
temp[k] = arr[p];
p++;
}else{
temp[k] = arr[q];
q++;
}
k++;
}
while(p<mid) temp[k++] = arr[p++];
while(q<n) temp[k++] = arr[q++];
memcpy(arr,temp,n*sizeof(int));
}
void merge_sort(int* arr,int n){
if(n<=1) return;
int mid = n/2;
merge_sort(arr,mid);
merge_sort(arr+mid,n-mid);
merge(arr,n,mid);
}
1.3 时间复杂度
一共拆分log2n次,每次比较n个元素,一共比较nlog2n次。
1.4 空间复杂度
随着n的增长,排序需要增加额外空间n+log2n(临时数组n和递归调用函数栈log2n),空间复杂度为O(n)。
2. 快速排序
- 左游标是
i
哨兵,右游标是j
哨兵。
- 第一次交换
- 第二次交换
- 基准交换
2.1 步骤
- 根据基准元素重排数组
int partition(int arr[],int n);
- 依次排列两个部分
void quick_sort(int arr[],int n);
2.2 参考代码
int partition(int arr[],int n){
int key = arr[0];
int p = 0,q = n-1;
while(p<q){
while(p<q && arr[q]>=key) q--;
arr[p] = arr[q];
while(p<q && arr[p]<=key) p++;
arr[q] = arr[p];
}
arr[p] = key;
return p;
}
void quick_sort(int arr[],int n){
if(n<=1) return;
int pivot = partition(arr,n);
quick_sort(arr,pivot);
quick_sort(arr+pivot+1,n-pivot-1);
}
// 快速排序
int partition(int* arr,int n){ // 基准点排序
int priot = arr[0]; // 设置第一个元素为基准
int i=0,j=n-1;
while(i<j){
while(i<j && arr[j]>=priot) --j;
while(i<j && arr[i]<=priot) ++i;
if(i<j){
swap(arr[i],arr[j]);
}
}
swap(arr[0],arr[i]);
return i;
}
void quick_sort(int* arr,int n){
if(n<=1) return;
int pos = partition(arr,n); // 基准点
quick_sort(arr,pos); // 基准点左边
quick_sort(arr+pos+1,n-(pos+1)); // 基准点右边
}
2.3 时间复杂度
一共拆分log2n次,每次比较n个元素,一共比nlog2n较次。
2.4 空间复杂度
随着n的增长,排序需要增加额外空间log2n(递归函数栈空间),空间复杂度为O(log2n)。
2.5 优化
交换指针法
int partition(int arr[],int n){
int key = arr[0];
int p = 0,q = n-1;
while(p<q){
while(p<q && arr[q]>=key) q--;
while(p<q && arr[p]<=key) p++;
if(p>=q) break;
swap(arr+q,arr+p);
}
swap(arr,arr+q);
return q;
}
3. 希尔排序
3.1 原理
- 给定一个长度n的列表,选择一定的步长
gap
,将列表分成若干个子列表sublist
。
例如:长度n=9
步长gap=3
分成3个子列表sublist
。
2. 对每一个子列表sublist
进行插入排序。
3. 依次减小步长gap,重复上述操作。直到gap为1。
希尔排序比插入排序的优势:
通过分组排序使元素逐渐靠近最终位置,从而减少了插入排序 时的移动次数。(先粗调再微调)
3.2 步骤
- 划分间距
gap
并执行排序
void shell_sort(int arr[],int n);
- 根据间距
gap
执行插入排序
void insertion_sort(int arr[],int n,int gap);
- 根据间距
gap
插入
void insert(int arr[],int n,int gap);
3.3 参考代码
void insert(int arr[],int n,int gap){
for(int i=n-1-gap;i>=0;i-=gap){
if(arr[i+gap]<arr[i]){
swap(arr+i+gap,arr+i);
}else{
break;
}
}
}
void insertion_sort(int arr[],int n,int gap){
for(int i=gap;i<=n;++i){
insert(arr,i,gap);
}
}
void shell_sort(int arr[],int n){
int gap = n;
do{
gap = gap/2;
insertion_sort(arr,n,gap);
}while(gap>1);
}
比较:插入排序与希尔排序
3.5 优化
移动代替交换
void insert(int arr[],int n,int gap){
int key=arr[n-1];
int i=0;
for(i=n-1;i>=gap&&arr[i-gap]>key;i-=gap){
arr[i] = arr[i-gap];
}
arr[i] = key;
}
void insertion_sort(int arr[],int n,int gap){
for(int i=gap;i<n;++i){// 插入元素i操作
insert(arr,i+1,gap);
}
}
void shell_sort(int arr[],int n){
for(int i=n/2;i>0;i/=2){// gap序列
insertion_sort(arr,n,i);
}
}
4. 小结
5. 算法选择标准
如何选择排序算法?(定性)