直接插入排序
直接插入排序,Straight Insertion Sort,将数据分为已排序区间和未排序区间,每次从未排序区间取出一个元素,插入到已排序区间的合适位置,直到所有元素都被排序完成。
代码实现:
void StraightInsertionSort(int* a, int n)
{
for (int i = 0; i < n - 1; i++)
{
//i为已排序区间的个数。
int end = i;
int tmp = a[end + 1];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + 1] = a[end];
}
else
{
break;
}
end--;
}
//插入已排序区间
a[end + 1] = tmp;
}
}
性能分析:
时间复杂度是
O
(
N
2
)
O(N^2)
O(N2),最优情况下(顺序有序)为
O
(
N
)
O(N)
O(N),最坏的情况下(逆序有序)为
O
(
N
2
)
O(N^2)
O(N2),而且在部分数据有序的时候,运行次数明显降低。
希尔排序
希尔排序是一种改进的插入排序算法,它通过比较相距一定间隔的元素进行排序,最终缩小间隔直至为1,完成最后一次插入排序。
在前面的学习中,我们知道直接插入排序对于部分有序的排序性能是非常高的,但如果是完全逆序那么每一个元素要调整的次数就非常多。
希尔排序的思想是改变元素的调整的步距,来使得逆序情况下每个元素需要调整的次数减少。
如下:
int arr[]={9,8,7,6,5,4,3,2,1,0};
void ShellSort(int*a,int n)
{
int gap=3;
for(int i=0;i<n-gap;i++)
{
int end=i;
int tmp=a[end+1];
while(end>=0)
{
if(tmp<a[end])
a[end+1]=a[end];
else
break;
}
a[end]+1=tmp;
}
}
观察上面数组arr,如果使用直接插入排序,那么元素3需要调整的次数为6次,但现在只需要2次.
虽然整个数组并未处于有序的状态,但是却变成了部分有序的状态,这样之后再次使用直接插入排序所需要运行的次数就大大减少。
那么gap的设置应该为多少呢,这个一直都是充满争议的问题,因为个人喜好不同而gap的选择也有不同。现在比较流行的gap取值有gap=n/2,gap=n/3+1。
代码实现:
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int i = 0; i < n - gap; i++)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
}
else
{
break;
}
end-=gap;
}
a[end + gap] = tmp;
}
}
}
希尔排序的时间复杂度计算非常复杂,经过前人的分析得出平均时间复杂度为
O
(
N
1.3
)
O(N^{1.3})
O(N1.3)。
显然希尔排序的性能是远远优于直接插入排序的。
在后续的文章中我也会对此进行分析。