通过学习《算法》,对几种排序算法做一个小结。首先展示不同算法的空间、时间等比较,后面对每一种具体描述。
- 选择排序
依此选择最小值从前往后排列,如图。
void mySort<Type>::selectSort(Type* root,int size)
{
int N = size;
int k;
for (int i = 0; i < N; ++i)
{
k = i;
for (int j = i + 1; j < N; ++j)
{
if (cmp(root[k], root[j]) > 0)
{
k = j;
}
}
if (k != i)
{
Type tmp = root[k];
root[k] = root[i];
root[i] = tmp;
}
}
}
- 插入排序
每次循环保证左边是排好顺序的。
void mySort<Type>::insertSort(Type* root, int size)
{
int N = size;
for (int i = 0; i < N; ++i)
{
for (int j = i + 1; j > 0; --j)
{
if (cmp(root[i], root[j]) > 0)
{
Type tmp = root[i];
root[i] = root[j];
root[j] = tmp;
}
}
}
}
- shell排序
还是以插入排序的思想,对h格进行排序。
template <typename Type>
void sort<Type>::shell_sort(Type *root, int size)
{
int i, j, grp;
Type temp;
for (grp = size / 2; grp > 0; grp /= 2) {
for (i = grp; i < size; i++) {
for (j = i - grp; j >= 0; j -= grp) {
if (cmp(root[j], root[j + grp]) > 0) {
temp = root[j];
root[j] = root[j + grp];
root[j + grp] = temp;
}
}
}
}
}
- 归并排序
注意“归并”二字,体现了算法的思想和方法,用到递归和合并。该算法的思想是:合并两个已排好序列为一个序列。基于此思想结合“分治思想”,将一个随机序列分为Length/2份子序列,分别对子序列排序、合并,直到将整个序列排序。
代码如下:
//--------原地归并抽象方法-------
template <typename Type>
void merge(Type *str, int lo, int mid, int hi) {
int i = lo, j = mid + 1;
Type* aux = new Type[hi+1];
for (int k = lo; k <= hi; ++k) //copy to aux[]
{
*(aux + k) = str[k];
}
for (int k = lo; k <= hi; ++k) //sort subsequence
{
if (i > mid) str[k] = aux[j++];
else if (j > hi) str[k] = aux[i++];
else if (aux[i] > aux[j]) str[k] = aux[j++];
else str[k] = aux[i++];
}
}
//up to button mergesort. 先分离再排序
template <typename Type>
void mergesort(Type *str, int lo, int hi) //recursive to sort input sequence
{
if (lo >= hi) return;
int mid = lo + (hi - lo) / 2;
mergesort(str, lo, mid); //sort the left of sequence
mergesort(str, mid+1, hi);
merge(str, lo, mid, hi);
}
//button to up mergesort. 先排序再合并
template <typename Type>
void mergesort(Type *str,int length) //no recursive
{
//int N = sizeof(str)/sizeof(str[0])-1;
/*记住一定要减1,因为此方法算得长度是包含'\0',所以要减1,才是有效长度。
但是通过指针传入,sizeof(str)只是指针的大小,和操作系统有关,
不能通过此方法获取数组长度。*/
for(int sz = 1;sz < N;sz = sz + sz)
{
for(int lo = 0;lo < N-sz;lo += sz + sz)
{
merge(str, lo, lo + sz - 1, (lo+sz+sz-1>N-1)?N-1:lo+sz+sz-1);
}
}
}
- 快速排序
也是一种“分治”的思想,过程是将一个sequence分为两段,分别排序后,整个sequence自然有序,和mergesort相互补充。
对比:
mergesort: 递归调用发生在sequence处理之前
quicksort: 递归调用发生在sequence处理之后
template <typename Type>
int partition(Type *str, int lo, int hi)
{
int i = lo,j = hi+1;
Type v = str[lo];
while(true)
{
while(str[++i]<v) if(i==hi) break; //思考为啥是++i和--j而不是i++和j--。
while(str[--j]>v) if(j==lo) break;
if(i>=j) break;
Type ex = str[i];
str[i] = str[j];
str[j] = ex;
}
Type ex = str[lo];
str[lo] = str[j];
str[j] = ex;
return j;
}
template <typename Type>
void quicksort(Type *str, int lo, int hi)
{
if(hi<=lo) return;
int j = partition(str, lo, hi);
quicksort(str, lo, j-1);
quicksort(str, j+1, hi);
}
快速排序的改进
和大多数递归排序一样,基于以下两点:
- 对于小数组,快速排序比插入排序慢
- 因为递归,快速排序的quicksort()会调用自己