r[i]=r[j]
,且r[i]
在r[j]
之前,而在排序后的序列中,r[i]
仍在r[j]
之前,则称这种排序算法是稳定的;否则称为不稳定的。插入排序,一般也被称为直接插入排序。
对于少量元素的排序,它是一个有效的算法。
直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
插入排序的平均时间复杂度是 O(n^2)
,空间复杂度为常数阶 O(1)
,具体时间复杂度和数组的有序性是有关联的。
插入排序中,当待排序数组是有序时,是最优的情况,只需当前数跟前一个数比较一下就可以了,这时一共需要比较 N-1
次,时间复杂度为 O(N)
。最坏的情况是待排序数组是逆序的,此时需要比较次数最多,最坏的情况是 O(n^2)
。
假设前面 n-1
(其中 n>=2
)个数已经是排好顺序的,现将第 n
个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第 n
个数的这个序列也是排好顺序的。
按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序。
从小到大的插入排序整个过程如图示:
第一轮:从第二位置的 6 开始比较,比前面 7 小,交换位置。
第二轮:第三位置的 9 比前一位置的 7 大,无需交换位置。
第三轮:第四位置的 3 比前一位置的 9 小交换位置,依次往前比较。
第四轮:第五位置的 1 比前一位置的 9 小,交换位置,再依次往前比较。
…
就这样依次比较到最后一个元素。
// 直接插入排序
// 时间复杂度:O(N^2) 逆序
// 最好的情况:O(N) 顺序有序
void InsertSort(int* a, int n)
{
// [0, end] end+1
for (int i = 0; i < n - 1; i++)
{
int end = i;
int temp = a[end + 1];
while (end >= 0)
{
if (temp < a[end])
{
a[end + 1] = a[end];
--end;
}
else
{
break;
}
}
a[end + 1] = temp;
}
}
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
void TestInsertSort()
{
int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };
InsertSort(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
int main()
{
TestInsertSort();
return 0;
}
O(N^2)
O(1)
,它是一种稳定的排序算法希尔排序,也称缩小增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
希尔排序的基本思想是:首先选择一个整数作为增量(gap),将待排序序列分成若干个子序列,每个子序列的元素之间距离为增量。然后对每个子序列进行插入排序。接下来,逐渐减小增量,重复上述分组和排序的过程。当增量减小到 1
时(就是直接插入排序),所有记录都在同一组内排好序。
希尔排序的算法步骤可以分为以下两步:
gap
。gap
。1
时,对整个数组进行直接插入排序,使数组完全有序。希尔排序的时间复杂度与初始增量gap
的选择和减小策略有关。选择合适的增量序列可以使得希尔排序的性能接近于最佳可能的线性时间复杂度。当前gap的减小策略以gap = gap/3 + 1
较好,其它减小策略如:gap = gap/2
也可行,但唯一一点就是要保证最后gap
的值最终能够减小到 1
。
希尔排序
//void ShellSort(int* a, int n)
//{
// int gap = n;
//
// // gap > 1时是预排序,目的让数组接近有序
// // gap == 1是直接插入排序,目的是让数组有序
// while (gap > 1)
// {
// //gap = gap / 2;
// gap = gap / 3 + 1;
// // 一组一组排
// for (int j = 0; j < gap; j++)
// {
// for (int i = j; i < n - gap; i += gap)
// {
// int end = i;
// int temp = a[end + gap];
// while (end >= 0)
// {
// if (temp < a[end])
// {
// a[end + gap] = a[end];
// end -= gap;
// }
// else
// {
// break;
// }
// }
// a[end + gap] = temp;
// }
// }
// }
//}
// 希尔排序(优化为多组并排,减少一层循环,关键在于i的变化)
// O(N^1.3)
void ShellSort(int* a, int n)
{
int gap = n;
// gap > 1时是预排序,目的让数组接近有序
// gap == 1是直接插入排序,目的是让数组有序
while (gap > 1)
{
// 保证最后gap的结果为1
//gap = gap / 2;
gap = gap / 3 + 1;
// 多组并排
for (int i = 0; i < n - gap; i++)
{
int end = i;
int temp = a[end + gap];
while (end >= 0)
{
if (temp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = temp;
}
}
}
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
void TestShellSort()
{
int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };
ShellSort(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
int main()
{
TestShellSort();
return 0;
}
gap > 1
时都是预排序,目的是让数组更接近于有序。当gap == 1
时,数组已经接近有序的了,这样进行直接插入排序就会很快。这样整体而言,可以达到优化的效果。gap
的取值方法很多,导致很难去计算,因此在许多书中给出的希尔排序的时间复杂度都不固定:
【八大排序】系列正在火热跟新中,大家敬请期待!!