我目前正在学习我的第一个算法类,最近我们开始谈论合并排序。我们的教授向我们展示了合并排序的伪代码与哨兵值,但并没有真正解释他们的目的。我仍然相当混淆他们服务的目的,因为我们的一个功课问题是编写没有哨兵值的合并排序。包含哨兵价值是否有任何优点或缺点?任何帮助将不胜感激。
编辑:就像一张纸条,我之前编写了一个合并排序,并且没有哨兵价值,这是导致我混淆的一部分。
答案 0 :(得分:3)
在合并排序中使用标记使我们无需检查是否已到达正在排序的任一阵列的末尾。可以在没有标记的情况下执行合并排序,但是没有标记的合并排序实现会为比较循环的每次迭代添加额外的检查。
例如:
我将在Cormen,Leiserson,Rivest和Stein的“算法简介和分析”中对此进行描述,使用两叠扑克牌来表示要排序的两个数组。
我们假设这两堆卡片都是面朝上的并且排序成每个堆中最小的卡片位于顶部,每堆中最大的卡片位于底部。
让我们打电话给左手堆牌阵列' L'和右手堆的卡阵列' R'。
对于合并排序的每次迭代,我们将比较每个堆上的顶部卡并确定哪个是最小的。我们将最小的卡从堆中取出,并将其面朝下放置以创建一堆新的分类卡。这是合并排序的首要原则。
但是,在我们能够对每一堆上的顶牌进行比较之前,我们必须确保每一堆上都有一张牌。例如,如果左侧堆中的牌一直小于右侧堆中的牌,我们可以在触及右侧堆中的任何牌之前将左侧牌中的牌抽出。在我们的代码中,这意味着当我们继续尝试查看数组中的下一个项目时,我们最终会尝试访问不存在的数组的索引,因为我们已经看过了所有数组的值。为了保护自己免受这种情况的影响,我们需要在合并排序中的每个比较之前加上一个检查,以确保每个数组中仍有值进行比较。
使用哨兵可以防止我们在每次比较之前需要执行额外检查,确保我们永远无法从一个或另一个堆中移除所有卡。如果我们使用“无限”'作为一个哨兵价值,那么“无限”'卡片永远不会从堆中移除,因为没有任何价值会超过“无穷大”。这样可以确保数组中的值始终是一个值来进行比较。
想想上面的例子:左边堆中的牌一直小于右边牌中的牌。左边的牌将一次一个地移除,直到“无限”。到达(哨兵)卡。然后,右边堆中的卡片将开始耗尽。
在某些时候,两个桩将拥有他们的无限'卡在上面。但是,到此为止,循环计数器将达到它的最大值,导致循环在它可以尝试访问不存在的数组索引之前终止。
答案 1 :(得分:0)
Sentinel值只是使它可以在1 for循环而不是3 for循环中进行合并。性能或内存的任何差异都很小,两种方法都是完全有效的;使用您喜欢的任何一种。