一、排序器:统一入口
template <typename T>
void Vector<T>:: sort(Rank lo, Rank hi){
switch(rand() % 5){
case 1: bubbleSort(lo, hi); break;
case 2: selectionSort(lo, hi); break;
case 3: mergeSort(lo, hi); break;
case 4: heapSort(lo, hi); break;
default: quickSort(lo, hi); break;
}
}
二、起泡排序
template<typename T>void Vector<T>::bubbleSort(Rank lo, Rank hi)
{while(!bubble(lo, hi--));}
- 改进:记录每扫描一趟后有没有交换元素位置,如果没有则表示前半部分元素已经是有序的,可以结束排序算法
template <typename T>bool Vector<T>::bubble(Rank lo, Rsnk hi){
bool sorted = true;
while(++lo < hi)
if(_elem[lo - 1] > _elem[lo]){
sorted = false;
swap(_elem[lo - 1], _elem[lo]);
}
return sorted;
}
- 再改进:存在一种特殊情况,乱序部分集中在未扫描部分的前部,在每次扫描中消耗多余时间在已经有序但尚未扫描的部分中。故提出改进方法,记录每一趟扫描中最后一次交换的逆序对的位置。
template <typename T> void Vector<T>::bubbleSort(Rank lo, Rank hi)
{while (lo < (hi = bubble(lo, hi)));}
template <typename T> Rank Vector<T>::bubble(Rank lo, Rank hi){
Rank last = lo;
while(++lo < hi)
if(_elem[lo - 1] > _elem[lo]){
last = lo;
swap(_elem[lo - 1], _elem[lo]);
}
return last;
}
- 综合评价
- 效率最好是O(n), 最坏是O(n^2)
- 输入含重复元素时,算法的稳定性(重复元素在输入、输出序列中的相对次序是否保持不变)是更为细致的要求。
- 起泡排序算法是稳定的。在起泡排序中,元素a和元素b的相对位置发生变化,只有一种可能。经分别与其它元素的交换中,二者相互接近直至相邻,在接下来的一轮扫描中,二者因为逆序而交换位置。
三、归并排序
- 原理
- 分治策略。
- 序列一分为二。O(1)
- 子序列递归排序。2*T(n/2)
- 合并有序子序列。O(n)
- 归并排序的允许时间为O(nlogn)
- 实现
template <typename T>
void Vector<T>::mergeSort(Rank lo, Rank hi){
if(hi - lo < 2) return;
int mi = (lo + hi) >> 1;
mergeSort(lo, mi);
mergeSort(mi, hi);
merge(lo, mi, hi);
}
template <typename T> void Vector<T>::merge(Rank lo, Rank mi, Rank hi){
T* A = _elem + lo;
int lb = mi - lo; T* B = new T[lb];
for(Rank i = 0; i < lb; B[i] = A[i++]);
int lc = hi - mi; T* C = _elem + mi;
for(Rank i = 0, j = 0, k = 0; (j < lb) || (k < lc);){
if((j < lb) && (lc <= k || (B[j] <= C[k])))A[i++] = B[j++];
if((k < lc) && (C[k] < B[j])))A[i++] = C[k++];
}
delete [] B;
}
![二路归并演示](https://img-blog.csdnimg.cn/20210716145156568.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTc4MTIyOA==,size_16,color_FFFFFF,t_70#pic_center)
- 二路归并改进:B提前耗尽,因为C本来就是A的一部分,故此时归并可直接结束。
for(Rank i= 0, j = 0, k = 0; (j < lb); ){
if((k < lc) && (C[k] < B[j])) A[i++] = C[k++];
if((lc <= k) || (B[j] <= C[k])) A[i++] = B[j++];
}
- 复杂度分析
- merge()总体迭代不超过O(n)次,累计只需线性时间
- 注意:待归并序列不必等长,即允许 lb ≠ lc, mi ≠ (lo + hi) / 2