您的问题最直接的答案是是的,它是插入排序。这是一个非常低效的插入排序,但它仍然是插入排序。
您的代码缺少确定性步骤,即一旦确定了元素的位置,比较就可以停止,然后对排序序列进行移位操作,从而为新元素留下一个洞。相反,您依靠比较循环为您执行该转换,即使不再需要比较也是如此,这不是很有效。
这可能看起来有点令人困惑,所以我将针对您的代码进行详细说明。
-
i 每次迭代的潜在客户元素最初是 a[i]。
- 您对序列中已排序的部分进行线性枚举,寻找
a[i] 所属的位置
- 一旦找到该位置(除非它已经是它所属的位置),您将
a[i] 与当前驻留在目标中的元素 a[j] 交换。
- 从那时起,
a[i] 的原始值现在就在序列中,但是...
- 对于排序序列的其余部分,交换比较保证会针对存储在
a[i] 中的任何值触发为真(提示:为什么要这样做?),因为之前成功的值已经排序。因此,a[i] 不断被排序序列中的下一个值替换,直到它最终保持最大值,即根据定义它所属的位置。
因此,是的,这就是插入排序。它在整体开始时维护一个排序序列,该序列随着每次主要迭代而不断扩展。并且对于每次主要迭代,前景元素都被“插入”,并且尾随元素被向下移动以形成可用的孔来执行此操作。
...这种方法的比较或交换次数更多/更少?
相当需要与您的方法进行更多比较。每次迭代都保证线性 O(n) 复杂度,并且有n 次迭代。因此,保证您的比较具有 O(N^2) 复杂度,这是高效排序算法的困扰。不只是最坏的情况; 保证。
C++ 插入排序
也就是说,考虑一下
template<typename Iter>
void insertion_sort(Iter first, Iter last)
{
for (Iter it = first; it != last; ++it)
std::rotate(std::upper_bound(first, it, *it), it, std::next(it));
}
如果您刚开始使用 C++,这可能是 seems like Greek(对希腊人没有冒犯),但它使用了两种使其非常高效的基本算法:std::upper_bound 和 std::rotate。
std::upper_bound 对排序序列进行操作。利用这一点,它可以利用 二分搜索 算法来定位排序序列中严格大于潜在值 (*it) 的第一个元素。因此,寻找单个潜在客户的插入点是O(logN),远远优于O(n)的线性搜索。
一旦知道插入点,std::rotate 就会使用插入点的迭代器将元素放置到位。它有效地做到了这一点:
0 1 2 3 5 6 4
^ ^ * these will be rotated right one element
0 1 2 3 5 6
4
0 1 2 3 5 6
4
0 1 2 3 4 5 6
请注意,旋转需要没有比较。
显然,这个模板解决方案不是有人会为一些补救算法课程提交的。但我希望它能给您一些关于插入排序如何通过以下方式最小化其比较的想法:
- 对序列中已排序的部分使用二分搜索来减少比较。
- 在执行轮换时使用 no 比较。
祝你好运。