排序是一种常用的数据结构,当有人问我具体有哪些排序呢?我说有选择、冒泡、快排、希尔……说着说着自己就觉得乱乱的,还是来一个系统的总结吧!
一、排序分类
- 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。
- 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。
注:由于排序种类很多,从本篇博客开始,下面几篇博客都将总结排序
二、直接插入排序(以升序为例)
1.算法思想
将要排序的优先序列分为两个区间,第一个区间为:有序序列区间;第二个区间为:无序序列区间。从无序序列中取一个元素,将它放入有序区间的合适的位置,然后重复此过程,直到第二个区间里面没有数据为止。
2.算法具体的步骤
(1)从第一个元素开始,该元素可以认为已经被排序
(2)取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果已排序好的元素大于新元素,将该已排序好的元素移到下一位置
- 重复此过程,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置中
(3)重复上述步骤,直到第二个区间里面没有元素为止
3.图解举例
4.代码实现
//直接插入排序
void Insert(int* arr, int size)
{
if (arr == NULL || size <= 0)
return;
for (int i = 1; i < size; ++i)
{
int end = arr[i];
int j = i;
while (j>0 && arr[j - 1] > end)
{
arr[j] = arr[j - 1];
j--;
}
arr[j] = end;
}
}
5.其他
(1)时间复杂度:O(n^2)
注:O(n^2)的这种情况是要排序的序列为降序时,这种情况是最差的一种情况。
当排序的序列是升序序列时,时间复杂度为O(1),这种情况是最好的一种情况。
我们在说时间复杂度时,要求考虑最差的一种情况!!!
(2)空间复杂度:O(1)
(3)稳定性:稳定
(4)使用场景:当要排序的序列数据较少,并且数据接近有序或者已经有序时。
6.动态图
三、二分插入排序
我们可以用二分查找来提高查找插入位置的位置,来对插入排序进行优化,简称二分插入排序。
代码实现如下:
//二分查找排序
void BinaryInsertSort(int* arr, int size)
{
if (arr == NULL || size <= 0)
return;
for (int i = 1; i < size; ++i)
{
int left = 0;
int right = i - 1;
int end = arr[i];
int mid = left + ((right - left) / 2);
while (left <= right)
{
if (arr[i]>mid)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
for (int j = i - 1; j >= left; --j)
{
arr[j + 1] = arr[j];
}
arr[left] = end;
}
}
四、希尔排序(递减增量排序算法)
1.算法思想
在对数据进行直接排序之前,先对数据进行预排序,使之接近有序,然后再利用直接插入排序对数据进行快速排序。
说明:从本质上来说,希尔排序是直接插入排序的一种优化,因为在直接排序算法中有可能出现最坏的一种情况,希尔排序就是为了防止这种情况。
总的来说,希尔排序是基于插入排序的以下两点性质而提出了改进方法:
(1)插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
(2)但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。
2.算法具体的步骤
基于直接插入排序,步骤如下:
(1)现将待排序的序列分成若干个子序列(该子序列由相隔某个增量的元素组成),对每一个子序列进行直接排序;
(2)依次缩减增量,再进行直接排序;
(3)待整个序列基本有序时(即增量足够小),对整体再次进行直接插入排序。
说明:
上面我们说到了增量这个概念,通过大佬们的实践,该增量(gap)一般情况下是这样计算的:第一次以排序序列中的元素除以3加1得到,之后每一次的增量都是前面增量除以3加1,直到增量为1结束
3.图解举例
4.代码实现
//希尔排序
void ShellSort(int* arr, int size)
{
if (arr == NULL || size <= 0)
return;
int gap = size;
while (gap >= 0)
{
gap = gap / 3 + 1;
for (int i = gap; i < size; i++)
{
int j = i - gap;
int end = arr[i];
if (arr[j]>arr[j+gap])
{
arr[j + gap] = arr[j];
}
else
{
break;
}
j -= gap;
arr[j + gap] = end;
}
}
}
5.其他
(1)时间复杂度:希尔排序介于O(N)~O(N^2),希尔排序,当N大时,平均的时间复杂度,大约在N^1.25–1.6N^1.25之间。
(2)空间复杂度:O(1)。
(3)稳定性:不稳定。
(4)使用场景:希尔排序是对插入排序的一种优化,当数据元素比较多时,适合希尔排序。