插入排序
对一个已经有序的数据序列插入一个数。要求插入后此数据序列仍然有序。插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据。
插入算法把要排序的
数组
分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
基本思想
:
每步将一个待排序的数据,按其关键码值的大小插入到前面已经排序好的文数据中的适当位置上,直到全部插入完为止。
时间复杂度:
最好:有序的
,有N个数据,则需N-1次比较。O(N-1)~O(N)
最坏:逆序的,当插入第N个数据时,需要(1+n-1)*(n-1)/2次比较(等差数列)。O(N*2)
是一种稳定的算法。
该算法适用于少量数据的排序
算法的稳定性是指:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
void InsertSort(int* a,size_t n)
{
//将[end+1]插入到[0,end]的有序区间
for(size_t i = 0; i < n-1;++i) //注意边界条件end为下标n-2
{
int end = i;
int tmp = a[end+1];
while(end >= 0)
{
if(a[end] > tmp)
{
a[end+1] = a[end];
--end;
}
else
{
break;
}
}
a[end+1] = tmp;
}
}
void TestInsertSort()
{
int a[] = {4,8,1,3,9,0,2,5,7,6};
cout<<"插入排序前:"<<endl;
PrintArray(a,sizeof(a)/sizeof(a[0]));
InsertSort(a,sizeof(a)/sizeof(a[0]));
cout<<"插入排序后:"<<endl;
PrintArray(a,sizeof(a)/sizeof(a[0]));
}
插入排序的优化----希尔排序
对插入排序的最坏情况进行优化。希尔排序的实现有多种方法。
分为两步:1)预排序(目的:使之接近有序):预排序是多次的,逐渐收敛,接近有序
2)最终排序(插入排序)
时间复杂度:
希尔排序的时间复杂度与增量(即,步长gap)的选取有关。例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为O(N²),而Hibbard增量的希尔排序的时间复杂度为O(N
3/2
)。
最好,逆序时,O(N)
是一种不稳定的算法,
对于相同的两个数,可能由于分在不同的组中而导致它们的顺序发生变化。
适合排序逆序的数据。
void ShellSort(int* a,size_t n)
{
assert(a);
int gap = n;
while(gap > 1)
{
gap = gap/3+1; //当n<3时,即gap=1,就成了插入排序。
//gap会逐渐缩小,最后gap=1(就成了插入排序)
for(size_t i = 0; i < n-gap; ++i)
{
int end = i;
int tmp = a[end+gap];
while(end >= 0)
{
if(a[end] > tmp)
{
a[end+gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end+gap] = tmp;
}
}
}
void TestShellSort()
{
int a[] = {4,8,1,3,9,0,2,5,7,6};
cout<<"希尔排序前:"<<endl;
PrintArray(a,sizeof(a)/sizeof(a[0]));
InsertSort(a,sizeof(a)/sizeof(a[0]));
cout<<"希尔排序后:"<<endl;
PrintArray(a,sizeof(a)/sizeof(a[0]));
}
程序运行结果: