1. 直接插入法
思路:将一个记录插入到已排序号的有序表中。先将序列中的第一个记录看成是一个有序的子序列,然后从第二个记录其逐个进行插入,直至整个序列变成按关键字排列的有序序列。
//pArry[i]表示当前要判定的元素,将其与其前面已排序好的所有元素相比,
//计算出该元素需要插入的位置j,后移pArry[j]至pArry[i - 1]元素
void InsertSort(int * pArry, int iLen)
{
int i, j, k, c;
int temp;
for (i = 1; i < iLen; i++) //指向当前需要判定的元素
{
for (j = 0; j < i; j++) //与该元素之前的所有元素比较,找出该元素要插入的位置
{
if (pArry[i] < pArry[j]) //pArry[i]元素应该插在pArry[j]元素所在的位置
{
//保存当前要插入的值
temp = pArry[i];
//记录后移
for (k = i, c = i - j ; c > 0; c--)
{
pArry[k] = pArry[k - 1];
--k;
}
//插入到正确的位置
pArry[k] = temp;
}
}
}
}
直接插入排序的时间复杂度为O(n^2)。当待排序记录的数量n很小时,这是一种很好的排序方法。但记录数量n很大时,则不宜采用直接插入排序。
2. 折半插入法
思路:直接插入排序法中的“查找”操作可以利用“二分查找”来实现(因为前面的子序列已经是有序序列),由此进行的插入排序称之为折半插入排序。
//pArry[i]表示当前要判定的元素,将其与其前面已排序好的所有元素相比,
//计算出该元素需要插入的位置j,后移pArry[j]至pArry[i - 1]元素
void BinaryInsertSort(int * pArry, int iLen)
{
int i, j, k, c;
int temp;
for (i = 1; i < iLen; i++) //指向当前需要判定的元素
{
//折半查找需要插入的位置
int low = 1;
int high = i;
while (low <= high)
{
int mid = (low + high) / 2;
if (pArry[i] > pArry[mid - 1])
{
low = mid + 1;
}
else if (pArry[i] < pArry[mid - 1])
{
high = mid - 1;
}
else
{
break;
}
}
j = (low + high) / 2; //找到需要插入的位置
temp = pArry[i];
//记录后移
for (k = i, c = i - j ; c > 0; c--)
{
pArry[k] = pArry[k - 1];
--k;
}
//插入到正确的位置, k==j
pArry[k] = temp;
}
}
折半插入排序所需附加存储空间和直接插入排序相同,折半插入排序仅减少了关键字的比较次数,而记录的移动次数不变。因此其时间复杂度仍然为O(n^2)。
3. 希尔排序(缩小增量排序)
设待排序元素序列有n个元素,首先取一个整数dk(dk < n)作为间隔, 将全部元素分为dk个子序列,所有距离为dk的元素放在同一个子序列中,然后对每一个子序列进行直接插入排序。然后缩小间隔dk,重复上述的子序列划分和排序工作。
void ShellSort(int * pArry, int ilen)
{
int i, j;
for (int dk = ilen / 2; dk > 0; dk /= 2) //排序趟数, dk为间隔距离
{
for (i = dk; i < ilen; i++) //对子序列进行直接插入排序
{
int temp = pArry[i];
for (j = i; j >= dk && temp < pArry[j - dk]; j -= dk)
{
pArry[j] = pArry[j - dk];
}
pArry[j] = temp;
}
}
}
增量的取法有各种方案,应用不同的序列会使希尔排序的性能有很大差异,当n < 1000时,希尔排序具有很高的效率,当n很大时排序码平均比较次数和元素平均移动次数大约在n^1.25到1.6n^1.25的范围内。希尔排序算法是一种不稳定的算法。