插入排序
1.直接插入
从左往右比较依次选出下一个数插入到前面有序区间里合适的位置,当第一次插入时,前面有序区间就是第一个值。
void InsertSort(int *a, size_t n)
{
int begin = 0;
int end = n - 1;
while (begin<end)
{
int i = begin;
int tmp = a[i+1];
while (i>=0&&a[i]>tmp)
{
a[i + 1] = a[i];
--i;
}
a[i + 1] = tmp;
++begin;
}
}
时间复杂度
挪动的次数*数组长度
- 逆序有序排序时间复杂度最坏O(n^2),每次比较都要比较和向前挪动整个有序区间所有的数。
- 顺序有序排序时间复杂度最好O(n)。
适合接近顺序有序的区间进行排序
2.希尔排序
该方法又称缩小增量排序
- 预排序 — 接近有序
思想
- 把小的数据尽可能往前面排,把大的数据尽可能往后排,这样在进行最终插入排序时,后面的数就不会很小,这样挪动的次数就减小了。
方法
- 取一个小于n的整数gap(gap又叫做步长),通过把所有距离为gap或者gap的倍数的数据放在一个组里,对每一个组进行插入排序,当每个组有序时,这整个序列就接近有序,然后减小gap,继续上面的操作,gap越小时,预排序完毕越接近有序,当gap等于1时,预排序就相当于直接插入排序。
![]()
void ShellSort(int* a, size_t n)
{
//预排序
int gap = 3;
for(int i = 0;i<n-gap;i++) //从序列a的左边往右轮循的对距离为gap的这些分组进行预排序
{
int end = i;
int tmp = a[end+gap];//每次选出本组里的下一个值,并与前面的值比较并挪动后插入。
while(end>=0&&a[end]>tmp)
{
a[end+gap] = a[end];
end-=gap;
}
a[end+gap] = tmp;
}
}
- 直接插入排序
但上面预排序完毕后还不是有序的,只有当gap为1时,排出来的序列才是有序的。
void ShellSort(int *a, size_t n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1; 加1,是因为如果最后gap为1或者2,那么除3就是0了,所以要加1,保证gap最小为1.
int begin = 0;
int end = n - gap; //因为现在是对距离gap为一组的记录进行排序
while (begin<end)//轮循的把每个组的每个位置都判断一遍,一次预排序
{
int i = begin;
int tmp = a[i + gap];
while (i >= 0 && a[i] > tmp)//对一个组的某个位置向前比较,并进行直接插入
{
a[i + gap] = a[i];
i -= gap;
}
a[i + gap] = tmp;
++begin;
}
}
}
时间复杂度
- 希尔排序的时间复杂度与gap增量函数有关,大概是O(n^1.3)
- 相比直接插入排序,顺序情况下不如直接插入,直接插入顺序情况时间复杂度是O(n);
- 逆序情况就比插入排序更优;逆序时,直接插入每次要多动所有的有序序列,时间复杂度为O(n^2)。
- 但希尔排序是不稳定,一次插入排序是不会改变相同元素的位置,但是多次插入排序,就会改变不同组里的相同元素的相对位置,因此希尔排序时不稳定。