常见排序:
- 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
- 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] = r[j] ,且 r[i] 在 r[j] 之前,而在排序后的序列中,r[i] 仍在 r[j] 之前,则称这种排序算法是稳定的;否则称为不稳定的。
- 内部排序:数据元素全部放在内存中的排序。
- 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。
目录
插入排序类:
插入排序类的基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
就如,玩扑克牌时,通过抽出扑克牌然后移动再插入的操作。是从排序2张,3张,4张……一样。
1、 直接插入排序:
当每一张扑克牌都成为一个元素时:当插入第 i ( i >= 1 )个元素时,前面的数组元素:array[ 0 ], array[ 1 ], …, array[ i - 1 ]已经排好序,此时用 array[i] 与前面已经排序好的:array[ i - 1 ], array[ i - 2 ], …, array[ 0 ], array[ 1 ]的数组元素进行顺序大小比较,找到插入位置并将array[i]插入,而原来位置上的元素顺序后移。
//直接插入排序
void InsertSort(int* a, int n)
{
assert(a);
//i循环:对每个数据排序
for (int i = 0; i < n - 1; ++i) //(将需要调整的数据定义为 i + 1, 而第一个元素由于为单,所以不需要排序,所以i[0, n-1])
{
int end = i;
//tmp:所需要调整的数据
int tmp = a[end + 1];
while (end >= 0)
{
//<是升序,>是降序
if (tmp < a[end])
{
//如果符合条件,就向后移动数据
a[end + 1] = a[end];
--end;
}
else
{
break;
}
}
//当出循环就找到,其所应该存在的位置,即放入数据
a[end + 1] = tmp;
}
}
1. 1 直接插入排序的特性:
-
元素集合越接近有序,直接插入排序算法的时间效率越高
-
时间复杂度:O(N^2)(最好为O(N) ~ 最差为O(N^2))
-
空间复杂度:O(1)
-
稳定性:稳定
1.1.1 时间复杂度:
最好O(N)的情况:
(有序即为最好)只有待排序值的前值皆与其有序,即不需要移动,所做的只需要判断该值较前值一次,并且每个元素都一样,即只需要每个元素的一次判断,即O(N)。
最差O(N^2)的情况:
(反序即为最差)只有待排序值的前值皆与反序,即前面有多少数据就需要移动几次,那这样就是一个等差数列:1 + 2 + 3 + … + N-1,则是:n (1+n-1)/2 则~O(n^2) 。即需要每个元素的一次判断,并移动的次数为最大,即O(N^2)。
1.1.2 空间复杂度:
即:空间复杂度为O(1)
1.1.3 稳定性:
2、 希尔排序:
希尔排序的基本思想:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排列完毕。
以gap = 2为例:
//希尔排序
/* 调整,下标 0~n 的数据,当调整下标为 X 的数据时,0~X-1 的数据已经排序完成 */
/* 平移数据: 当大于(小于),则将元素向后调整。*/
void ShellSort(int* a, int n)
{
assert(a);
int gap = n;
while (gap > 1)
{
// +1防止gap无法到1,当之前gap = 2时,变为3 / 2 = 0,(即由2步变为了0)
gap = gap / 3 + 1;
/*gap = gap / 2*/
//j:代表与i同一组(根据gap划分)的数组元素下标
//i:代表即将调整的元素下标,作为每一组比较数据的最后一个元素下标
for (int j = 0; j < gap; j++)
{
for (int i = j; i < n - gap; i += gap)
{
int end = i;
//tmp:调整的数据
int tmp = a[end + gap];
while (end >= 0)
{
//<是升序,>是降序
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
}
希尔排序的另外一种写法:
以gap = 2为例:
除比较次序的变化以外,其余部分皆相同。
//希尔排序
void ShellSort(int* a, int n)
{
assert(a);
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
/*gap = gap / 2*/
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];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
2.1 希尔排序的特性总结:
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
- 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定:
《数据结构(C语言版)》--- 严蔚敏
《数据结构-用面相对象方法与C++描述》--- 殷人昆
- 稳定性:不稳定
- 空间复杂度:O(1)
2.1.1 空间复杂度:
即:空间复杂度为O(1)