插入排序
遍历待排序数据集合,每次将元素插入到其前面已排序数据集合的合适位置。
插入排序与打麻将类似,在整理最初摸起来的13张牌过程中,不断将未整理的牌插入至已整理的牌的合适位置。
插入算法的核心思路是不断地将元素插入至合适的位置。
若将数据集合按照从小到大的进行排序,代码如下:
template<typename T>
void insertSort(std::vector<T>& data)
{
for (size_t i = 1; i < data.size(); ++i) {
T tmp = std::move(data[i]);
size_t j = i;
while (j > 0 && (tmp < data[j - 1])) {
data[j] = data[j - 1];
--j;
}
data[j] = std::move(tmp);
}
}
对于插入排序,时间复杂度为O(n^2)
。当数据集合是有序集合时,插入排序的内层for循环的判断条件将不成立,时间复杂度将优化为O(n)
。插入排序非常适合对部分有序的数据集合进行处理。插入排序的适合场景为:
- 数据集中每个元素距离其最终的位置都不远;
- 数据集合中只有几个元素的位置不正确;
- 一个有序的大数据集合接一个随机排序的小数据集合。
希尔排序
插入排序非常适合对部分有序的数据集合进行处理。插入排序是循环遍历数据集合,不断将数据插入至已排序数据集合中的合适位置。插入排序在每步处理完成后数据集合前一部分将有序,而后一部分数据待排序。
那么,能不能让数据集合在整体上越来越有序呢?希尔排序应运而生,希尔排序是插入排序的改进。希尔排序又称缩小增量排序,通过比较相距一段间隔的元素进行排序,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。
若将数据集合按照从小到大的进行排序,代码如下:
template<typename T>
void shellSort(std::vector<T>& data)
{
auto size = static_cast<std::ptrdiff_t>(data.size());
std::ptrdiff_t interval = size / 2;
while (interval >= 1) {
for (std::ptrdiff_t i = interval; i < size; ++i) {
T tmp = std::move(data[i]);
std::ptrdiff_t j = i;
while ((j - interval >= 0) && (tmp < data[j - interval])) {
data[j] = data[j - interval];
j -= interval;
}
data[j] = std::move(tmp);
}
interval /= 2;
}
}
与插入排序相比,希尔排序效率很高,当随机数据集合个数为100000时,希尔排序和插入排序的测试结果如下: