简单插入排序的基本思想:
-
设待排序表为
L[0...n-1],初始时子表L[0]为有序表 -
对于剩下长度为n-1的有序表
L[1...n-1],将每个元素插入L前面的有序表中恰当的位置,保持L前部有序,即找到位置p,然后将L[p]以及有序表中L[p]以后的元素向后移动一个位置,最后将这个元素放到L[p] -
步骤2执行
n-1次后,L排序完毕
针对这一思想,有两种实现方式,即对于如何在有序表中找到待插入元素的位置可以是顺序查找,也可以是二分查找,这两种方式时间复杂度都为O(n^2)
于是有如下实现
void simple_Insertion(int A[], int length)
{
for (int i = 1; i < length; ++i)
{
if (A[i] < A[i - 1]) //如果待插入元素的插入位置不是有序表尾,则在有序表中查找
{
int j = i;
int e = A[i];
for (; j > 0 && e < A[j - 1]; --j)
A[j] = A[j - 1];
A[j] = e;
}
}
}
// binary search
void search_optInsertion(int A[], int length)
{
for (int i = 1; i < length; ++i)
{
if (A[i] < A[i - 1])
{
int lo = 0, hi = i - 1;
while (lo < hi) //这里当下标从0开始时hi和mid的计算和下标从1开始不同
{
int mid = (lo + hi) / 2;
if (A[i] < A[mid])
hi = mid;
else
lo = mid + 1;
}
int e = A[i]; //仍然需要移动大量元素
for (int j = i; j > lo; --j)
A[j] = A[j - 1];
A[lo] = e;
}
}
}
因为简单的插入排序对于基本有序的表进行排序效果非常好,时间复杂度为O(n),并且由于操作简单,对于元素少的无序表效果也很好,所有就有了希尔排序
希尔排序的基本思想是利用增量把一个长的表划分成多个小的表,分别对这些小的表进行排序,然后增量逐渐减小到1,退化为简单插入排序,此时表已经基本有序,故时间复杂度会显著降低
从这个算法的描述上着手,我们需要的只是在简单插入排序的基础上外面再套一层循环用来进行增量递减的工作,就能实现希尔排序了
因此我们有如下实现
void shell(int A[], int length)
{
for (int dk = length / 2; dk > 0; dk /= 2) //这里采用的增量递减策略是倍减
{
for (int i = dk; i < length; ++i) //简单插入排序时表头元素为有序,现在是前dk个元素有序
{
if (A[i] < A[i - dk])
{
int e = A[i];
int j = i;
for (; j - dk >= 0 && e < A[j - dk]; j -= dk) //这里j - dk大于等于0,最后会退化成j > 0 (即j - 1 >= 0)
A[j] = A[j - dk];
A[j] = e;
}
}
}
} //所以,这个算法去掉外侧循环并且dk改为1后和简单插入排序是一模一样的
浙公网安备 33010602011771号