ChatGPT解决这个技术问题 Extra ChatGPT

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

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

出于并行化目的?例如:归并排序是稳定的,可以很好地并行化,快速排序也是如此。
经典快速排序不稳定
稳定排序算法 - IBM (Insertion, Bubble, Merge)
给像我这样可能误解这个概念的人的注释:保证相等元素的顺序被保留。意思是:如果稳定排序中的元素被认为是相等的,那么它们将遵循先前的顺序。这不是我以前想的那样:如果之前顺序中的元素被认为是相等的,那么在即将到来的稳定排序中,它们将遵循之前的顺序。尽管您可能会发现后一种理解在许多情况下也很有意义。

S
Sergio

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

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

peach
straw
apple
spork

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

apple
peach
straw
spork

不稳定排序算法中,strawspork 可以互换,但在稳定的排序算法中,它们保持相同的相对位置(也就是说,因为 straw 出现在 spork 之前在输入中,它也出现在输出中的 spork 之前)。

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

现在回答你的问题,假设我们有一个名字和姓氏的列表。我们被要求“按姓氏排序,然后按名字排序”。我们可以先按名字排序(稳定或不稳定),然后按姓氏稳定排序。在这些排序之后,列表主要按姓氏排序。但是,如果姓氏相同,则对名字进行排序。

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


@user1416486:我们仅按第一个字母排序。有了这个假设,strawspork 比较相等。稳定排序将保留输入的顺序,而不稳定排序则不能保证。 “正确”取决于应用程序。大多数编程语言中的排序功能允许用户提供自定义排序功能。如果用户的函数将不同的项目视为相同的(例如,相同的名字,不同的姓氏),则有助于了解是否会保留原始订单。有关真实示例,请参见 OCaml's array sorting functions
我不明白这一行..相同的排序键?这里的钥匙是什么意思?请解释语句..相同的排序键
@saplingPro:通过“排序键”,我的意思是你排序项目的东西。所以当按首字母排序时,那么对于每个项目,它的“排序键”就是它的首字母。
@JoeyAdams 您能否将评论中的信息添加到您的答案中。我正要对此投反对票,因为 spork 确实在 straw 之前,除非您只按第一个字母排序。对我来说,这不是对字符串进行排序的自然方式,应该明确说明。
示例 - 假设您有一个列表,其中每个项目都包含有关航班目的地和出发时间的信息。您首先根据时间对列表进行排序。然后我们根据目的地对其进行排序。如果第二类是稳定的,我们现在将所有航班绑定到同一个目的地,并且按照起飞时间的递增顺序。如果它不稳定,它们就不会按时间递增的顺序排列。
s
snr

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

由于某些人不了解演示文稿的逻辑的一些反馈,我再次需要编辑问题。它说明了对第一个元素进行排序。另一方面,您可以考虑由键值对组成的插图。

稳定的排序算法:

插入排序

合并排序

冒泡排序

蒂姆排序

计数排序

块排序

四边形

图书馆排序

鸡尾酒调酒器

侏儒排序

奇偶排序

不稳定的排序算法:

堆排序

选择排序

壳排序

快速排序

Introsort(服从快速排序)

树排序

循环排序

平滑排序

比赛排序(以Hesapsort为准)

https://i.stack.imgur.com/alljt.png


你的价值观不一样。您比较 9,7 和 9,8,但根据稳定性检查,您需要相同的值,例如 9,7 或 9,8。在稳定的算法中,相同的值应该以相同的顺序排列。
不,要检查稳定性,您的值应该相同。我的意思是假设您使用两个 9,7 并将其命名为节点 A 和节点 B。如果每个排序操作顺序都像 A、B (而不是它们相等),请理解排序算法是稳定的(如归并排序)。如果 A,B 顺序在多次排序时发生变化(1. 排序 A,B 然后 B,A 再 A,B 等),请了解排序算法是不稳定的(如快速排序)@snr
@snr [9, 6] 不在输入数组中。我认为您的意思是 [9, 8] 在最后一个数组条中。
@erhun 我相信他只按第一个数字(逗号前的那个)排序,并使用第二个数字作为参考,让您看到第一个 9 与第二个 9 不同。
@erhun 什么定义元素相同?这正是使用的排序标准!它可以是你想要的任何人。我的标准是所有能被 10 整除的数字都是相等的,无论是 20 还是 500
B
Bob Murphy

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

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

如果您不需要稳定性,您可以使用库中的快速、占用内存的算法,例如堆排序或快速排序,而不必理会它。

如果你需要稳定性,那就更复杂了。与不稳定算法相比,稳定算法具有更高的 big-O CPU 和/或内存使用率。因此,当您拥有大型数据集时,您必须在 CPU 或内存之间做出选择。如果您在 CPU 和内存方面都受到限制,那么您就有问题了。一个好的折衷稳定算法是二叉树排序; Wikipedia article 具有基于 STL 的极其简单的 C++ 实现。

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


像 Merge Sort 这样的稳定算法具有与 Quicksort 相同的 O(NlogN) 复杂度;不过,努力的常数乘数更大。
是的,合并排序的内存使用量为 O(N),而快速排序的内存使用量为 O(log N)。我提到 Quicksort 的原因是 qsort() 是一个 C 标准库例程,因此它很容易使用。
最佳整体答案恕我直言。其他人提到的多键技术很有趣,但被高估了;它应用起来很简单,但往往比明显的替代方案慢得多(只需使用一个带有多键比较的排序;或按第一个键排序,然后识别并排序任何具有重复项的子列表)。稳定排序产生可预测结果的事实在某些应用程序中可能很重要。特别是如果您有两个相同的输入列表 A、B,除了列表 B 有一个额外的条目之外,稳定排序的输出将是相同的,除了 B 具有相同的额外条目。最后一个pgph +1。
在最后一句话中,我不明白您所说的“每条记录的最后一个键”是什么意思-您能解释一下吗?总体上非常好的信息性评论:)
@augenss 如果两条记录都有键“foo”,那么在进行排序之前,将它们更改为“foo_00001”和“foo_00002”之类的东西。当您进行排序时,这将保留两个键的原始顺序。然后,当您完成排序后,将两个键都改回“foo”。
L
Levent Divilioglu

这取决于你做什么。

想象一下,您有一些带有名字和姓氏字段的人员记录。首先,您按名字对列表进行排序。如果您随后使用稳定的算法按姓氏对列表进行排序,您将得到一个按名字和姓氏排序的列表。


C
Clinton Pierce

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


记录交换与稳定性有什么关系?
如果您保留订单,那么对于某些输入,它可能会有更少的“搅动”元素,这会导致额外的内存页面写入...... FWIW
r
roottraveller

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

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

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


J
John R Perry

我知道对此有很多答案,但对我来说,Robert Harveythis answer 总结得更清楚:

稳定排序是保留输入集的原始顺序的排序,其中 [不稳定] 算法不区分两个或多个项目。

Source


M
M Ciel

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

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

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

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

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

PS:当然,两次排序的方法并不是解决特定问题的最佳方法,但要解释海报的问题就足够了。


r
rcgldr

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

一个类似的例子是经典的 Excel,它一次将排序限制为 3 列。要对 6 列进行排序,首先对最不重要的 3 列进行排序,然后对最重要的 3 列进行排序。

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

后来(1937 年)出现了卡片整理(合并)机器,可以通过比较字段来合并两副卡片。输入是两副已经分类的牌,一个主牌和一个更新牌。整理者将这两个卡片组合并为一个新的主库和一个存档库,该库可选地用于主副本,以便新主库只有在出现重复时才会有更新卡。这可能是原始(自下而上)合并排序背后的想法的基础。


L
Luka Rahne

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

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

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

稳定的排序算法是必要的确定性的。


这不是稳定的意思。请参阅en.wikipedia.org/wiki/Sorting_algorithm#Stability
我应该更正最后一句,即使在相同的实现中,非稳定排序也可以输出不同的解决方案,其中任何稳定排序都输出相同的解决方案。
为什么 -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 这是关于每个稳定排序都有的好属性的更多声明。也就是说,无论使用稳定排序算法还是实现,每次都会有相同的结果。在不同的非稳定排序实现中很难维护这种属性。