排序算法的稳定性是什么,为什么如此重要?


292

我很好奇,为什么稳定性在排序算法中很重要?


2
为了并行化?例如:合并排序是稳定的,可以很好地并行化,快速排序也是如此。
DarthVader

13
经典QuickSort不稳定
Konstantin Spirin

9
稳定排序算法-– IBM (Insertion, Bubble, Merge)
roottraveller

给那些可能会误解像我这样的概念的人的注释:保证平等元素的顺序得到保留。意思是:如果稳定排序的元素被认为是相等的,则它们将遵循先前的顺序。 这不是我以前想的:如果认为前顺序中的元素相等,那么在接下来的稳定排序中,它们将遵循前顺序。尽管您可能会发现后一种理解在许多情况下也很有意义。
瑞克(Rick)

Answers:


371

如果两个具有相同关键字的对象在出现在要排序的输入数组中时,它们在排序输出中以相同顺序出现,则认为排序算法是稳定的。有些排序算法本质上是稳定的,例如插入排序,合并排序,冒泡排序等。而有些排序算法则不是稳定的,例如堆排序,快速排序等。

背景技术:“稳定”的排序算法使具有相同排序关键字的项目保持顺序。假设我们有一个包含5个字母的单词的列表:

peach
straw
apple
spork

如果我们仅按每个单词的第一个字母对列表进行排序,则将产生稳定排序:

apple
peach
straw
spork

在一个不稳定的排序算法,straw或者spork可以互换,但在稳定的一个,它们留在相同的相对位置(即,由于straw出现之前spork在输入,它也出现之前spork在输出)。

我们可以使用这种算法对单词列表进行排序:按第5列,第4列,第3列,然后2列,然后按1列进行稳定排序。最后,将对其进行正确排序。说服自己。(顺便说一下,该算法称为基数排序)

现在回答您的问题,假设我们有一个名字和姓氏列表。我们被要求按“姓,然后名”排序。我们可以先按名字排序(稳定或不稳定),然后再按姓氏进行稳定排序。经过这些排序后,列表主要按姓氏排序。但是,在姓氏相同的地方,名字会被排序。

您不能以相同的方式堆叠不稳定的排序。


那么,该怎么称呼才能使苹果桃子运动秸秆的单词正确排序呢?稳定的排序给了我们苹果桃秸秆,但是st应该在sp之后(按字母顺序正确),因此最终的正确排序应该是苹果桃子运动秸秆
user1416486 2012年

2
@ user1416486:我们仅按首字母排序。有了这个假设,straw并且spork比较相等。稳定的排序将保留输入的顺序,而不稳定的排序则无法保证输入的顺序。“正确”取决于应用程序。大多数编程语言中的排序功能使用户可以提供自定义排序功能。如果用户功能将不同的项目视为相同(例如,相同的名字,不同的姓氏),则有助于知道是否保留原始顺序。有关实际示例,请参见OCaml的数组排序功能
乔伊·亚当斯

3
我不明白这行..相同的排序键吗?您在这里所说的钥匙是什么意思?请解释该声明..相同的排序键
saplingPro 2012年

2
@saplingPro:“排序键”是指您正在对项目进行排序的东西。因此,当按首字母排序时,对于每个项目,其“排序关键字”即为其首字母。
乔伊·亚当斯

12
示例-假设您有一个列表,其中每个项目都包含有关航班目的地和出发时间的信息。您首先要根据时间对列表进行排序。然后,我们根据目的地对其进行排序。如果第二种情况是稳定的,那么我们现在将所有航班一起飞往同一目的地,并且按出发时间的升序排列。如果不稳定,他们的时间就不会增加。
roottraveller

55

稳定的排序算法是一种将相同元素按照与输入中出现的相同顺序进行排序的算法,而不稳定的排序算法可能无法满足这种情况。- 感谢我的算法讲师Didem Gozupek对算法的深入了解

稳定的排序算法:

  • 插入排序
  • 合并排序
  • 气泡排序
  • 蒂姆·索特(Tim Sort)
  • 计数排序
  • 块排序
  • 四排序
  • 图书馆排序
  • 鸡尾酒杯排序
  • 侏儒排序
  • 奇偶排序

不稳定的排序算法:

  • 堆排序
  • 选择排序
  • 贝壳类
  • 快速分类
  • Introsort(取决于Quicksort)
  • 树排序
  • 循环排序
  • 平滑排序
  • 比赛排序(取决于Hesapsort)

在此处输入图片说明


2
您的价值观不平等。您比较9,7和9,8,但根据稳定性检查,您需要相同的值,例如9,7或两者都9,8。而且,在稳定算法中,相同值应按相同顺序排序。
Erhun

1
不,要检查稳定性,您的值应该相同。我的意思是假设您使用两个9,7并将其分别命名为节点A和节点B。如果每个排序操作顺序都像A,B(而不是相等)一样,则了解排序算法是稳定的(例如合并排序)。如果在对它们进行多次排序时A,B顺序发生变化(1.对A,B进行排序,然后对B,A再次对A,B等进行排序),请理解排序算法是不稳定的(如快速排序)@snr
erhun

@snr [9,6]在输入数组中不存在。我想您的意思是最后一个数组带中的[9,8]。
Usman

4
@erhun我相信他仅按第一个数字(逗号前的数字)进行排序,并使用第二个数字作为参考,以使您看到前9个与第二个9不同
。– Tiago

20

排序稳定性意味着具有相同键的记录在排序之前和之后均保持其相对顺序。

因此,只有当您要解决的问题需要保持该相对顺序时,稳定性才重要。

如果不需要稳定性,则可以使用堆排序或快速排序等库中的快速内存提取算法,而不必理会。

如果需要稳定性,则更加复杂。稳定算法比不稳定算法具有更高的big-O CPU和/或内存使用率。因此,当您拥有大量数据集时,您必须在击败CPU或内存之间做出选择。如果您在CPU和内存上都受限制,那么您会遇到问题。一个好的折衷稳定算法是二叉树排序。在维基百科的文章具有基于STL一个可怜容易C ++实现。

通过将原始记录号添加为每条记录的最后一位关键字,可以将不稳定的算法变成稳定的算法。


1
像Merge Sort这样的稳定​​算法与Quicksort具有相同的O(NlogN)复杂度;但是,持续努力的乘数更大。
乔纳森·勒夫勒

是的,合并排序上的内存使用量为O(N),而快速排序上的内存使用量为O(log N)。我提到Quicksort的原因是qsort()是C标准库例程,因此可以立即使用。
鲍勃·墨菲

1
最佳总体答案恕我直言。其他人提到的多键技术很有趣但被高估了;它很容易应用,但往往比明显的替代方法慢得多(只需使用一种带有多键比较的排序方式;或者按第一个键排序,然后识别并排序所有重复的子列表)。在某些应用中,稳定排序会产生可预测的结果这一事实可能很重要。特别是,如果您有两个输入列表A,B相同,除了列表B具有一个额外的条目,则稳定排序的输出将相同,除了B具有相同的额外条目。最后pgph +1。
greggo

16

这取决于您的工作。

假设您有一些人的姓名和姓氏记录。首先,您按名字对列表进行排序。然后,如果您使用姓氏以稳定的算法对列表进行排序,则将有一个列表按姓氏和姓氏排序。


4
我认为您的意思是“姓氏和名字”。姓通常是姓。
培根片

14

稳定性之所以重要很重要。一种是,如果不需要通过交换两条记录来交换它们,则可能导致内存更新,页面被标记为脏页,并且需要重新写入磁盘(或另一种慢速介质)。


记录交换与稳定性有什么关系?
user1683793

4

如果两个具有相同键的对象在输入输出中的顺序与在输入未排序数组中出现的顺序相同,则该排序算法被认为是稳定的。有些排序算法本质上是稳定的,例如插入排序,合并排序,冒泡排序等。而有些排序算法则不是稳定的,例如堆排序,快速排序等。

但是,任何给定的不稳定排序算法都可以修改为稳定。可以使用特定的排序算法来使其稳定,但是通常,可以通过更改键比较操作将本质上不稳定的任何基于比较的排序算法修改为稳定,以便将两个键的比较视为位置。具有相同键的对象的系数。

参考文献:http : //www.math.uic.edu/~leon/cs-mcs401-s08/handouts/stability.pdf http://en.wikipedia.org/wiki/Sorting_algorithm#Stability



1

如果您假设排序只是数字,而只有它们的值可以识别/区分它们(例如,具有相同值的元素是相同的),那么排序的稳定性问题就毫无意义。

但是,排序时具有相同优先级的对象可能是不同的,有时它们的相对顺序是有意义的信息。在这种情况下,不稳定的排序会产生问题。

例如,您有一个数据列表,其中包含所有玩家在游戏中用级别[L]清理迷宫的时间成本[T]。假设我们需要按照清洁迷宫的速度对玩家进行排名。但是,还有一条附加规则:较高级别清洁迷宫的玩家始终具有较高的等级,而无论时间花费多长时间。

当然,您可以尝试使用遵循规则的某种算法将配对值[T,L]映射为实数[R],然后使用[R]值对所有玩家进行排名。

但是,如果可以进行稳定排序,则可以简单地按[T](首先是快速播放器)然后按[L]对整个列表进行排序。在这种情况下,将玩家按其清理的迷宫级别分组后,其相对顺序(按时间成本)将不会更改。

PS:当然,两次排序的方法并不是解决特定问题的最佳解决方案,但解释发布者问题应该足够了。


0

稳定的排序将始终在相同的输入上返回相同的解决方案(排列)。

例如,[2,1,2]将使用稳定的排序作为排列[2,1,3]进行排序(首先是索引2,然后是索引1,然后是已排序输出中的索引3),这意味着输出始终以相同的方式进行混洗。其他不稳定但仍然正确的排列是[2,3,1]。

快速排序不是稳定的排序,并且相同元素之间的排列差异取决于选择枢轴的算法。一些实现是随机选择的,并且可以使用相同的算法进行快速排序,从而在相同的输入上产生不同的排列。

稳定的排序算法是必须确定的。



我应该纠正最后一句话,因为即使任何稳定排序输出相同解决方案,即使在同一实现中,非稳定排序也可以输出不同的解决方案。
卢卡·拉涅

1
为什么为-1?有人可以指出这里有什么问题吗?这不是稳定排序的含义,而是属性稳定排序的含义。
卢卡·拉恩

排序是否是确定性的,并不能确定排序是否稳定。我可以通过定义不同的抢七式行为(例如,通过对非关键部分进行子分类)来编写一种不稳定的确定性排序算法。稳定排序特别意味着在对领带进行排序时,将保留元素的预排序相对顺序。稳定排序的输出示例:sort([(5,3),(1,5),(3,3),(1,3)], x) => [(1,5),(1,3),(3,3),(5,3)]。我可以进行始终(确定地)输出的确定性排序:[(1,3),(1,5),(3,3),(5,3)]但这不是稳定的排序。
考伯特(Cowbert)

@cowbert这是关于每个稳定排序都具有的漂亮属性的更多声明。无论使用稳定排序算法还是实施,每次都会得到相同的结果。在不同的非稳定排序实现中很难维护这种属性。
卢卡·拉内

0

需要稳定排序原因的更多示例。数据库是一个常见的例子。以交易数据库为例,包括姓氏,购买日期,商品编号,价格。假设数据库通常按日期|排序。然后进行查询以按姓氏排序后的数据库副本,因为稳定的排序会保留原始顺序,即使查询比较仅涉及姓氏,每个姓氏的事务也会按时间顺序。

类似的示例是经典Excel,它一次只能将排序限制为3列。要对6列进行排序,请使用最低3列进行排序,然后使用最高3列进行排序。

稳定的基数排序的经典示例是卡片排序器,用于按基数为10的数字列进行排序。卡从最低有效位到最高有效位排序。每次通过时,都会读取一副纸牌,并根据该列中的数字将其分成10个不同的纸箱。然后,将10个纸箱中的卡片按顺序放回输入料斗(“ 0”个卡片,“ 9”个卡片)。然后,下一列进行另一遍操作,直到所有列都已排序。实际的卡片分类器有10个以上的存储箱,因为卡片上有12个区域,一列可以为空白,并且存在误读的存储箱。要对字母进行排序,每列需要2次通过,数字第一次通过,12 11区域第二次通过。

后来(1937年),出现了一些卡片整理(合并)机器,它们可以通过比较字段来合并两副卡片。输入的内容是两个已经排序的卡片组,一个主卡片组和一个更新卡片组。整理器将两个卡片组合并为一个新的资料箱和一个存档箱,可以选择将其用于主副本,以便新主箱仅在有副本的情况下才具有更新卡。这可能是原始(自下而上)合并排序背后思想的基础。

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.