繁体   English   中英

插入排序还是选择排序的变体?

[英]Insertion sort or a variation of selection sort?

这里有一个代码片段。 测试了几个案例,似乎工作正常。

在学习算法之后,我已经一次性编写代码用于插入排序,但是对于这是否真的是传统的插入排序有疑问?

我有一种感觉,它可能是选择排序的变化(调整版本),这是我混淆的原因。

具体来说,这是关注的领域:(给定n元素的数组a

for(i=1;i<n;i++){
    for(j=0;j<i;j++){
        if(a[i] < a[j]){
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }      
} 

此外,这种方法的比较或交换数量是否更多/更少?

在此先感谢您的帮助。

此代码是插入排序实现。 看一下内循环:

for(j=0;j<i;j++){
    if(a[i] < a[j]){
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
} 

或简化:

for(j = 0; j < i ; j++)
    if(a[i] < a[j])
        swap(a[i], a[j]);

现在我们知道子阵列a[0:i - 1]已经从外部循环的上一次运行中排序。 这是一个逻辑高尔夫版本的查找索引n ,我们需要插入a[i]并推送索引在[n, i - 1]范围内的所有元素,其中一个索引更高:

while(j < i && a[j] <= a[i])
    j++;

//insert a[i] at the appropriate index
int tmp = a[j];
a[j] = a[i];

//push all elements starting from index n one index further
for(; j < i; j++){
    int swap = tmp;
    tmp = a[j];
    a[j] = swap;
}

这两个代码片段的逻辑等效如下:
直到搜索到的索引na[i]插入索引),不会发生交换。 现在我们换掉a[i]a[n] 从那时起, a[i]将等同于上述代码中的tmp变量。 由于数组的其余部分仍然按索引i - 1排序,我们现在将每个元素换成它的前一个元素,该元素当前存储在索引i 绝对是一些很好的高尔夫插入排序。

外环只是标准

for(i = 0; i < array_length; i++)
     insert a[i] at appropriate position

你的问题最直接的答案是肯定的 ,它是插入排序。 这是一种非常低效的插入排序,但它仍然插入排序。

您的代码缺乏确定的步骤,一旦确定元素的位置,比较就可以停止,并且对排序序列的移位操作随后为新元素创建一个洞。 相反,您依靠比较循环为您执行该转换,即使不再需要比较,这也不是非常有效。

这可能看起来有点令人困惑,所以我会详细说明你的代码。

  • 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 ++,这可能看起来像希腊语 (对希腊人没有冒犯),但它使用两种基本算法,使其效率惊人: std::upper_boundstd::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 

请注意,旋转不需要比较。

显然,这个模板解决方案不是某人会提交一些补救算法课程的东西。 但我希望它能为您提供有关插入排序如何通过以下方式最小化其比较的一些想法:

  • 在序列的已排序部分上使用二进制搜索以最小化比较。
  • 执行旋转时使用比较。

祝你好运。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM