插入排序与选择排序


109

我试图了解插入排序和选择排序之间的区别。

它们似乎都具有两个组成部分:未排序列表和已排序列表。他们似乎都从未排序列表中选取一个元素,并将其放入适当位置的已排序列表中。我见过一些网站/书籍说选择排序是通过一次交换一个来实现的,而插入排序只是找到合适的位置并插入它。但是,我看到其他文章说了一些话,说插入排序也会互换。因此,我感到困惑。有规范的资料吗?


8
选择排序的维基百科带有伪代码和漂亮的插图,插入排序的维基百科也有。
G. Bach 2013年

6
@ G.Bach-谢谢您。。。我已经阅读了两页,但是不明白它们之间的区别,因此提出了这个问题。
eb80

3
根据Computerphile,它们是相同的:youtube.com/watch?
Tristan Forward

Answers:


184

选择排序:

给定一个列表,获取当前元素并将其与当前元素右侧的最小元素交换。 选择排序

插入排序:

给定一个列表,将当前元素插入到列表的适当位置,并在每次插入时调整列表。这类似于在纸牌游戏中安排纸牌。 插入排序

选择排序的时间复杂度总是不变的n(n - 1)/2,而插入排序的时间复杂度最好,因为它的最坏情况是n(n - 1)/2。通常,然后需要进行更少或相等的比较n(n - 1)/2

来源:http//cheetahonfire.blogspot.com/2009/05/selection-sort-vs-insertion-sort.html


2
@Nikolay-这是从cheetahonfire.blogspot.com/2009/05/…复制的,我已经读过了。我读过有冲突的文章时,还有其他协奏曲吗?
eb80

5
主要区别在于选择步骤。选择排序选择最小的交换,然后与第一个交换。插入排序将当前的插入到适当的位置
Nikolay Kostov,2013年

6
@ eb80我不确定您正在阅读哪种材料,但我不知道一个示例比这更具体吗?
G. Bach 2013年

23
但是掉期的数量呢?选择始终执行n(n-1)/ 2个比较,但在最坏的情况下,它只会进行n-1个交换。在最坏的情况下,插入将执行n(n-1)/ 2个比较以及n(n-1)/ 2个交换。
詹森·古玛

2
@Adorn这些都不是分而治之的算法。
Asky McAskface

62

插入排序和选择排序都具有一个外循环(在每个索引上)和一个内循环(在索引的子集上)。内循环的每次遍历都会将排序区域扩展一个元素,但以未排序区域为代价,直到用尽未排序元素为止。

区别在于内部循环的作用:

  • 在选择排序中,内部循环位于未排序的元素上。每遍都选择一个元素,然后将其移动到其最终位置(在已排序区域的当前末端)。

  • 在插入排序中,内部循环的每个遍历都会迭代排序后的元素。已排序的元素将移位,直到循环找到正确的位置以插入下一个未排序的元素。

因此,在选择排序中,以输出顺序找到排序后的元素,并在找到它们后保持原样。相反,在插入排序中,未排序的元素将保持放置状态,直到按输入顺序消耗为止,而已排序区域的元素将继续四处移动。

就交换而言:选择排序每次对内循环进行一次交换。插入排序通常像temp 内部循环之前一样保存要插入的元素,为内部循环留出空间将已排序的元素上移一个,然后再复制temp到插入点。


23

选择排序
假设有一个以特定/随机方式写的数字数组,可以说我们要按升序排列。因此,一次取一个数字,然后用最小的数字代替。在列表中可用。通过执行此步骤,我们最终将获得期望的结果。

在此处输入图片说明



插入排序
请记住类似的假设,但是唯一的区别是,这次我们一次选择一个数字,然后将其插入预先排序的部分中,这减少了比较,因此效率更高。

在此处输入图片说明


只需在YouTube上查看mycodeschool。但是,此答案与youtube上2种算法的视频中介绍的内容相辅相成。
JaydeepW

21

造成混淆的原因可能是因为您正在将对链表进行排序的描述与对数组进行排序的描述进行比较。但是我不确定,因为您没有引用您的消息来源。

理解排序算法的最简单方法通常是获得该算法的详细说明(不要含糊不清,例如“这种排序使用交换。在某处。我不是说在哪里”),得到一些扑克牌(5-10应该足够了) (用于简单的排序算法),然后手动运行该算法。

选择排序:浏览未排序的数据以寻找剩余最小的元素,然后将其交换到紧接排序后的数据之后的位置。重复直到完成。如果对列表进行排序,则无需将最小的元素交换到位置,而是可以将列表节点从其旧位置移除,然后将其插入新位置。

插入排序:在排序后的数据之后立即获取元素,浏览排序后的数据以找到放置位置,然后将其放置在该位置。重复直到完成。

插入排序可以在其“扫描”阶段使用交换,但不是必须的,这不是最有效的方法,除非您对以下数据类型的数组进行排序:(a)无法移动,只能复制或交换;(b)复制比交换更昂贵。如果插入排序确实使用了swap,那么它的工作方式是同时搜索位置并将新元素放在此处,方法是将新元素与紧接其前的元素重复交换一次,只要它之前的元素大于它。到达不大的元素后,您将找到正确的位置,然后继续下一个新元素。


1
这非常有帮助...最后一段让我感到困惑,这是由于每种类型的变体引起的。
eb80

1
+1表示在链表上使用插入排序与在位数组上的交换效率完全不同
Gavin Achtemeier

11

两种算法的逻辑都非常相似。它们都在数组的开头具有部分排序的子数组。唯一的区别是他们如何搜索要放入已排序数组中的下一个元素。

  • 插入排序下一个元素插入正确的位置;

  • 选择排序选择最小的元素并与当前项目交换;

而且,与Selection Sort相反,Insertions Sort是稳定的。

我在python中都实现了,值得注意的是它们有多么相似:

def insertion(data):
    data_size = len(data)
    current = 1
    while current < data_size:
        for i in range(current):
            if data[current] < data[i]:
                temp = data[i]
                data[i] = data[current]
                data[current] = temp

        current += 1

    return data

进行很小的更改就可以做出选择排序算法。

def selection(data):
    data_size = len(data)
    current = 0
    while current < data_size:
        for i in range(current, data_size):
            if data[i] < data[current]:
                temp = data[i]
                data[i] = data[current]
                data[current] = temp

        current += 1

    return data

抱歉,我想知道为什么(选择排序算法)每当data [i]较小时,data [i]就与data [current]交换。在基本/原始(?)选择排序中,我们在range(i,data_size)中找到最小值,并以该最小值交换data [i] ...有点不同...
Tony Ma

4

简而言之,我认为选择排序首先搜索数组中的最小值,然后执行交换操作,而插入排序则采用一个值并将其与留在其后的每个值进行比较。如果值较小,则会交换。然后,再次比较相同的值,如果该值小于后面的值,它将再次交换。我希望这是有道理的!


4

简而言之,

选择排序:从未排序的数组中选择第一个元素,然后将其与其余未排序的元素进行比较。它类似于Bubble排序,但是不交换每个较小的元素,而是保持最小的元素索引更新,并在每个循环结束时交换它

插入排序:与选择排序相反,它从未排序的子数组中选择第一个元素,然后将其与已排序的子数组进行比较,然后插入找到的最小元素,并将所有已排序的元素从其右边移至第一个未排序的元素。


3

我将再尝试一次:考虑在几乎排序的数组的幸运情况下会发生什么。

在排序时,可以将数组视为具有两个部分:左侧-已排序,右侧-未排序。

插入排序-选择第一个未排序的元素,然后尝试在已排序的部分中为其找到位置。由于您是从右到左搜索的,因此很可能会发生以下情况:与之比较的第一个排序元素(最大的,最右边的部分在左侧)小于选择的元素,因此您可以立即继续下一个未排序的元素。

选择排序-选择第一个未排序的元素,然后尝试查找整个未排序部分的最小元素,并在需要时交换这两个元素。问题是,由于正确的部分没有排序,因此您每次都必须考虑每个元素,因为您可能无法确定是否存在比所选择的元素还要小的元素。

顺便说一句,这正是heapsort在选择排序时的改进之处-由于使用heap,它能够更快地找到最小的元素。


3

选择排序:在开始构建排序的子列表时,该算法确保排序后的子列表总是完全排序,不仅是根据其自身的元素,而且还包括完整数组(即排序和未排序的子列表)。因此,一旦从未排序子列表中找到了新的最小元素,就将其追加到已排序子列表的末尾。

插入排序:算法再次将数组分为两部分,但是这里是从第二部分中拾取元素并将其插入到第一部分的正确位置。这决不能保证第一部分按照完整的数组进行排序,尽管在最后的过程中每个元素当然都处于正确的排序位置。


3

两种算法通常都这样工作

步骤1:从未排序列表中获取下一个未排序元素

步骤2:将其放在排序列表中的正确位置。

对于一种算法,步骤之一更容易实现,反之亦然。

插入排序:我们将未排序列表的第一个元素放在排序列表的某处。我们知道在哪里放置下一个元素(未排序列表中的第一个位置),但是需要一些工作才能找到将其放置在哪里(某个地方)。步骤1很简单。

选择排序:我们采取的元素某处未排序的列表,然后把它放在排序列表的最后位置。我们需要找到下一个元素(它很可能不在未排序列表的第一个位置,而是在某处),然后将其放在排序列表的末尾。第2步很容易


2

插入排序的内部循环遍历已排序的元素(与选择排序相反)。这样,当找到正确的位置时,它可以中止内部循环。意思就是:

  1. 在一般情况下,内部循环将仅遍历一半的元素。
  2. 如果几乎对数组进行排序,则内部循环甚至将中止。
  3. 如果已经对数组进行了排序,则内部循环将立即中止,从而使插入排序的复杂度在这种情况下呈线性。

选择排序必须始终遍历所有内部循环元素。这就是为什么插入排序优先于选择排序的原因。但是,另一方面,选择排序执行的元素交换要少得多,这在某些情况下可能更重要。


1

基本上,插入排序是通过一次比较两个元素来完成的,而选择排序则从整个数组中选择最小的元素并对其进行排序。

从概念上讲,插入排序通过比较两个元素直到对整个数组进行排序来继续对子列表进行排序,而选择排序选择了最小元素并将其交换到第一个位置,第二个最小元素交换到了第二个位置,依此类推。

插入排序可以显示为:

    for(i=1;i<n;i++)
        for(j=i;j>0;j--)
            if(arr[j]<arr[j-1])
                temp=arr[j];
                arr[j]=arr[j-1];
                arr[j-1]=temp;

选择排序可以显示为:

    for(i=0;i<n;i++)
        min=i;
        for(j=i+1;j<n;j++)
        if(arr[j]<arr[min])
        min=j;
        temp=arr[i];
        arr[i]=arr[min];
        arr[min]=temp;

1

这两种排序算法的选择取决于所使用的数据结构。

使用数组时,请使用选择排序(尽管为什么,何时可以使用qsort?)。使用链接列表时,请使用插入排序。

这是因为:

  • 链表遍历比数组更昂贵。
  • 链表插入比数组便宜得多。

插入排序将新值注入到排序的段的中间。因此,需要将数据“推回”。但是,在使用链接列表时,通过扭曲2个指针,可以有效地将整个列表推回去。在数组中,您必须执行n-i交换才能将值推回,这可能会非常昂贵。

选择排序始终附加在末尾,因此在使用数组时不会出现此问题。因此,数据无需“推回”。


0

一个简单的解释如下:

给定:未排序的数组或数字列表。

问题陈述:以升序对数字列表/数组进行排序,以了解选择排序和插入排序之间的区别。

插入排序:您会从上到下查看列表,以方便理解。我们将第一个元素视为初始最小值。现在,我们的想法是,我们线性遍历该列表/数组的每个索引,以找出在任何索引处是否还有其他元素的值小于初始最小值。如果找到这样的值,我们只交换它们索引处的值,即假设15是索引1的最小初始值,并且在线性遍历索引期间,我们遇到了一个数值较小的数字,例如索引9处为7。现在,将索引9处的值7替换为值为15的索引1。该遍历将继续与当前索引的值进行比较,其余索引交换为较小的值。这一直持续到列表/数组的倒数第二个索引为止,

选择排序:假设列表/数组的第一个索引元素已排序。现在,从第二个索引处的元素开始,将其与先前的索引进行比较,以查看该值是否较小。遍历可以可视化为两个部分,已排序和未排序。对于列表/数组中的给定索引,将可视化从未排序到已排序的比较检查。假设您的索引1的值为19,索引3的值为10。我们考虑从未排序到已排序(即从右到左)的遍历。因此,假设我们必须对索引3进行排序。从右向左比较时,我们看到它的值小于索引1。一旦确定,我们就将索引3的这个数字10放在具有值19的索引1的位置。索引1的原始值19向右移了一位。

我没有添加任何代码,因为问题似乎在于了解遍历方法的概念。


0

插入排序不交换东西。即使它使用了temp变量,使用temp var的要点是,当我们发现索引中的值与前一个索引的值相比较小时,我们将较大的值移至较小值的位置会写东西的索引。然后,我们使用temp var替换上一个索引。示例:10、20、30、50、40。迭代1:10、20、30、50、50。[temp = 40]迭代2:10、20、30、40(温度值),50。只需在某个变量的所需位置插入一个值即可。

但是,当我们考虑选择排序时,我们首先找到具有较低值的索引,然后将其与第一个索引中的值交换,并继续重复交换直到所有索引都得到排序。这与两个数字的传统交换完全相同。示例:30,20,10,40,50。迭代1:10,20,30,40,50。此处temp var仅用于交换。


0

插入排序在交换该选择方面做得更多。这是一个例子:

在此处输入图片说明


0

它们两者的共同点是它们都使用分区来区分数组的排序部分和未排序的部分。

所不同的是,通过选择排序,可以确保在将元素添加到已排序分区中时,数组的已排序部分不会改变。

原因是,因为选择搜索未排序集的最小值,然后将其添加到排序集的最后一个元素之后,因此将排序集增加了1。

另一方面,插入仅关心关心的下一个元素,它是数组未排序部分中的第一个元素。它将使用此元素并将其简单地放入已排序集中的适当位置。

对于仅部分排序的数组,插入排序通常通常是更好的候选方法,因为您正在浪费时间来寻找最小值。

结论:

选择排序通过在未排序部分中找到最小元素,将元素逐渐添加到末尾。

插入排序会将未排序部分中找到的第一个元素传播到排序部分中的任何位置。


0

尽管选择排序和插入排序的时间复杂度相同,但为n(n-1)/ 2。平均性能插入排序更好。在我的i5 CPU上测试了30000个随机整数,选择排序平均花费1.5s,而插入排序平均花费0.6s。


欢迎使用StackOverflow,并感谢您的回答。您很可能会发现许多人已经通过视觉插图做出了不错的回答。例如,七年前的尼古拉·科斯托夫(Nikolay Kostov)指出,时间复杂度仅在最差的情况下才适用于插入排序。如果您认为他错了,我们欢迎您提供更多详细信息,以扩大您的答案。
Maxim Sagaydachny

-1

用外行人的话来说,(也许是最容易理解问题的最简单方法)

冒泡排序类似于排成一行并尝试按高度对自己进行排序。您一直在与旁边的人切换,直到您在正确的位置。这从左到右(取决于实现方式)一直发生,并且您一直切换直到每个人都被排序为止。

但是,在选择排序中,您正在做的事情类似于安排一副牌。您看一下卡片,取最小的卡片,一直放到最左边,依此类推。


3
他问的是“选择”和“插入”排序之间的区别
许定

-1

选择-选择一个特定的项目(最低的项目)并将其与第i(迭代次数)个元素交换。(即第一,第二,第三……),因此将排序列表放在一边。

插入-首先与第二个进行比较,然后与第二个进行比较,然后再将第三个与第二个进行比较,然后将第一个与第四个进行比较。

比较所有排序的链接

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.