基本思想:在一个已经排好序的记录子集的基础上,每一步将下一个人等待排序的记录有序插入到已经排序的记录子集上,知道所有待排序记录全部插入为止。
(1)直接插入排序:直接插入排序其实很简单,从第二个元素开始,逐步插入到已排序子序列中。其比较实用与排序记录数比较少的情况,排序数目较大时,该算法排序性能不太好。代码如下:
void InsertSort(int a[], int length)
{
for (size_t i = 2; i < length; i++)
{
a[0] = a[i];
int j = i - 1;
while (a[j] > a[0] )
{
a[j + 1] = a[j];
j--;
}
a[j+1] = a[0];
}
}
性能分析:时间复杂度: T(n) = O(n^2) 由于只需要一个人辅助空间a[0]所以 空间复杂度为:S(n) = O(1)
(2)折半插入排序:其实稍微改进了直接插入排序的性能,在寻找插入位置的时候使用折半查找。即直接插入排序的改进版。
void BinSort(int a[], int length)
{
for (size_t i = 2; i < length; i++)
{
a[0] = a[i];
int low = 1, high = i - 1;
while (low <= high)
{
int mid = (high +low) / 2;
if (a[mid] < a[0])
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
for (size_t j = i-1; j >= low; j--)
{
a[j + 1] = a[j];
}
a[low] = a[0];
}
}
性能分析:虽然改进了查找的次数,但是并未改变移动元素的时间耗费,所以,折半插入排序的时间复杂度: T(n) = O(n^2) 由于只需要一个人辅助空间a[0]所以 空间复杂度为:S(n) = O(1)
(3)希尔排序:希尔排序其实就是克服了直接插入排序的缺点—对于较大数量的排序记录仍然效率较好。
算法思想:
(a) 选定记录间的距离为d1在整个待排序记录序列中将所有间隔为d1的记录分成一组,进行组内直接插入排序。
(b) 取记录间的距离为d2(d2小于d1)在整个待排序记录序列中,将所有间隔为 d2的记录分成一组,进行组内直接插入排序。
(c)重复步骤2,直至记录间的距离d=1,此时整个只有一个子序列,对该序列进行直接插入排序,完成整个排序过程。
void ShellInsert(int a[], int length, int delta)
{
size_t j;
for (size_t i = 1 + delta; i < length; i++)
{
if (a[i] < a[i - delta])//0,18,5,60,43,54,90,46
{
a[0] = a[i];
for (j = i - delta; j > 0 && a[j] > a[0]; j-=delta )
{
a[j + delta] = a[j];
}
a[j + delta] = a[0];
}
}
}
void ShellSort(int a[], int length,int delta[],int n)
{
for (size_t i = 0; i < n; i++)
{
ShellInsert(a, length, delta[i]);//进行每一趟
}
}
性能分析:时间复杂度: T(n) = O(n^1.5) 由于只需要一个人辅助空间a[0]所以 空间复杂度为:S(n) = O(1)
(4)小结 :
排序算法 | 时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 是否稳定 |
---|---|---|---|---|---|
直接插入排序 | O(n^2) | O(n) | O(n^2) | S(1 | 稳定 |
折半插入排序 | O(n^2) | O(nlog2(n)) | O(n^2) | S(1) | 稳定 |
希尔排序 | O(n^1.5) | S(1) | 不稳定 |