我正在阅读Robert Sedgewick关于C ++算法中的合并排序,并有以下问题。
static void mergeAB(ITEM[] c, int cl, ITEM[] a, int al, int ar, ITEM[] b, int bl, int br )
{
int i = al, j = bl;
for (int k = cl; k < cl+ar-al+br-bl+1; k++)
{
if (i > ar) { c[k] = b[j++]; continue; }
if (j > br) { c[k] = a[i++]; continue; }
c[k] = less(a[i], b[j]) ? a[i++] : b[j++];
}
}
基本合并的特征值得注意的是 内循环包括两个测试,以确定是否结束 已达到两个输入数组。当然,通常这两个测试 失败,因此情况因使用哨兵钥匙而大声呼喊 允许删除测试。也就是说,如果元素具有键值 大于所有其他键的键被添加到a的末尾 和aux数组一样,测试可以删除,因为当a(b)数组时 耗尽,哨兵导致c数组的下一个元素 取自b(a)数组,直到合并完成。
然而,使用哨兵并不总是那么容易,因为它 可能不容易知道最大的键值或因为空间可能 不方便。
对于合并,有一个简单的补救措施。该方法基于 以下想法:鉴于我们已经辞职将数组复制到 实现就地抽象,我们只需将第二个数组放入 复制时的逆序(无需额外费用),以便它 相关索引从右向左移动。这种安排导致 最大的元素 - 无论是哪个数组 - 作为哨兵 另一个阵列。
我对上述文字的疑问
当a(b)数组耗尽时语句是什么?什么是“a(b)”?
为什么作者提到确定最大密钥并不容易确定最大密钥的空间?
作者的意思是“鉴于我们已经辞职复制数组”?什么是在这种情况下辞职?
请求用简单的例子来理解提到的简单补救措施?
答案 0 :(得分:3)
“当a(b)阵列耗尽时”是“当a
阵列或b
阵列耗尽时的简写”。
接口正在处理更大数组的子数组,所以你不能简单地写出数组的末尾。
代码将数据从两个数组复制到另一个数组中。由于这个副本是不可避免的,我们'不得不复制数组'意味着我们不情愿地接受必须复制数组是不可避免的。
整蛊......这需要一些时间才能弄明白是什么意思。
Tangentially :这可能不是我编写循环的方式。我倾向于使用:
int i = al, j = bl;
for (int k = cl; i <= ar && j <= br; k++)
{
if (a[i] < b[j])
c[k] = a[i++];
else
c[k] = b[j++];
}
while (i <= ar)
c[k++] = a[i++];
while (j <= br)
c[k++] = b[j++];
两个尾随循环中的一个没有做任何事情。修订后的主合并循环每次迭代有3次测试,而对于一次原始算法,每次迭代有4次测试。我没有正式测量它,但更简单的合并循环可能比原始的单循环算法更快。
前三个问题几乎最适合English Language Learners。
答案 1 :(得分:1)
有时,括号用于一次告诉一个或多个相似的短语:
当a(b)用尽时,我们从b(a)
复制元素
表示:
当a耗尽时,我们从b复制元素, 当b用尽时,我们从
复制元素
关于哨兵的两件令人讨厌的事情是
我们程序员永远不会乐意复制(或移动)周围的东西,如果可能的话,将它们留在已经存在的地方(因为我们很懒)。 在这个版本的合并排序中,我们已经放弃了尝试不复制的东西...我们辞职。 鉴于我们必须复制,如果我们愿意,我们可以以相反的顺序复制事物(当然,以相反的顺序使用副本),因为这是免费的(*)。
(*)在这个抽象级别是免费的,某些真实CPU的成本可能很高。几乎总是在表演区YMMV。