1. 直接插入排序
算法如下:
void insertSort(int A[], int n) //A[1..n], A[0]用作哨兵
{
int i, j;
for (i = 2;i <= n;i++)
{
if (A[i] >= A[i - 1])
continue;
A[0] = A[i]; //复制为哨兵
A[i] = A[i - 1];
for (j = i -2;A[j] > A[0];j--)
A[j + 1] = A[j]; //记录后移
A[j + 1] = A[0]; //插入到正确位置
}
}
算法的时间复杂度为O(n^2)。
当数组A[]中的元素有序时,算法进行的关键字比较次数大到最小值n-1,此时不需要移动记录。
2. 快速排序
算法如下:
int partition(int A[], int low, int high)
{
int pivotkey, temp;
pivotkey = low; //pivotkey = mid{A[low], A[(high + low) / 2], A[high]}
if (A[low + (high - low) / 2] > A[low])
{
pivotkey = low + (high - low) / 2;
}
if (A[high] > A[low + (high - low) / 2])
{
pivotkey = high;
}
temp = A[low];
A[low] = A[pivotkey];
A[pivotkey] = temp;
pivotkey = A[low]; //枢轴记录关键字
while (low < high) //从表的两端交替的向中间扫面
{
while (low < high && A[high] >= pivotkey) //将比枢轴记录小的记录移到低端
high--;
A[low] = A[high];
while (low < high && A[low] <= pivotkey) //将比枢轴大的记录移到高端
low++;
A[high] = A[low];
}
A[low] = pivotkey; //枢轴记录到位
return low; //返回枢轴位置
}
void qsort(int A[], int low, int high)
{
int pivotloc;
if (low < high)
{
pivotloc = partition(A, low, high);
if (pivotloc - low < high - pivotloc){ //先对长度短的子序列中的记录进行快速排序
qsort(A, low, pivotloc - 1);
qsort(A, pivotloc + 1, high);
}
else {
qsort(A, pivotloc + 1, high);
qsort(A, low, pivotloc - 1);
}
}
}
算法平均时间复杂度为O(n logn)。就平均时间而言,快速排序是目前被认为最好的一种排序方法。
当序列基本有序时,算法变成冒泡排序,时间复杂度为O(n^2)。
最好每次划分能得到两个相等的子序列,即每趟排序之后,pivotkey的位置应靠中间位置,因此pivotkey的选择影响算法的时间复杂度。因此pivotkey采用”三者取中“的办法,即取A[low]、A[(low + high) / 2]、A[high]三者中的中值作为枢轴值。经验证明采用三者取中的规则可大大改善快速排序在最坏情况下的性能。
在递归调用的过程中,若每趟排序之后,枢轴位置均偏向子序列的一端,则递归调用的深度大到最大值n。在调用的过程中若先对短的子序列中的记录进行快速排序,栈的最大深度可降为O(logn)
3. 堆排序(利用数组模拟堆)
代码如下:
/**
已知A[s..m]中的关键字除A[s]之外均满足堆的定义,
此函数调整A[s]使A[s..m]成为一个大顶堆
*/
void heapAdjust(int A[], int s, int m)
{
int rc, j;
rc = A[s];
for (j = s << 1;j <= m;j <<= 1)//沿关键字较大的孩子结点向下筛选
{
if (j < m && A[j + 1] > A[j])
j++; //j为关键字较大的记录的下标
if (rc >= A[j]) //当rc大于等于较大的关键字时结束
break;
A[s] = A[j]; //移动关键字
s = j;
}
A[s] = rc;
}
/**
对顺序表A[1..n]进行堆排序
*/
void heapSort(int A[], int n)
{
int i, temp;
for (i = n >> 1;i >= 1;i--) //构建堆
heapAdjust(A, i, n);
for (i = n;i > 1;i--)
{
temp = A[1]; //将堆顶记录和和当前未经排序子序列A[1..i]中的
//最后一个记录相互交换
A[1] = A[i];
A[i] = temp;
heapAdjust(A, 1, i - 1);//将A[1..i-1]重新调整为一个大顶堆
}
}
调整一个结点的过程的时间复杂度为O( logn)。
建立初始堆的过程就是反复调整结点的过程,时间复杂度为O(nlogn)。
对建好的对进行排序过程时间复杂度为O(nlog)。
因此总的时间复杂度为O(nlogn)。
在在最坏情况下的时间复杂度为O(nlogn),要好于快速排序。