1) 确保 n 个反转对的 n 次交换的插入排序。
2)假设颜色存储在桶中,给定 n 个桶,相似的颜色很有可能更靠近。这会导致数组部分排序的情况,如果反转次数 cn,则数组是部分排序的。
这种推理有两个问题。
首先是最坏情况下反转的次数不是O(n)。要了解原因,请假设您有 n/2 个蓝色鹅卵石,然后是 n/2 个红色鹅卵石(没有白色鹅卵石)。那么总的反转次数为(n2)/4。 (此外,从我刚刚运行的一些快速实验来看,即使是平均情况下的反转总数也是 (n2) /6 — 仍然不是 O(n)。)
这种推理的第二个问题是,您被要求将交换次数限制为恰好 n,而不是 O(n)。因此,即使插入排序只需要(比如说)2 次n 次交换,那仍然太多了。
2) [¶] 散列颜色并将颜色作为部分排序数组插入的散列函数是什么?我需要线性探测方法吗?
插入排序不需要哈希函数;而且我认为您无法在恒定额外空间的约束下构建任何有意义的哈希表。
相反,您可以只将三种颜色表示为(例如)0、1 和 2。
3) 为什么我们需要额外的空间?因为插入排序是就地算法
即使是插入排序也需要一些额外的变量,例如数组索引。
Alexander Anikin 已经提出了与我将建议的相同的解决方案,但我不确定他的回答是否清楚你如何自己提出这个问题,所以这个答案的其余部分将引导你通过那个。
所以,假设您从尝试插入排序开始。然后我们可以逐步改进它,直到它满足要求。 (这确实需要一些洞察力,但每个单独的改进只需要一点洞察力,而不是一次解决整个问题。)
第一个改进:color(i) 的调用次数。
当我们想将一个新的 pebble 插入到数组的已排序部分中时,我们需要找到它应该在该部分中的位置。
对于纯插入排序,我们通过检查数组中已排序部分中的卵石来做到这一点。但是我们只能准确地调用color n 次;我们预计必须为每个 pebble 至少调用一次 - 你不能在不检查的情况下对 pebble 进行排序 - 这意味着要完成这项工作,我们需要为每个 pebble 调用一次准确卵石。我们不能为我们已经分类的鹅卵石重新调用它。
要解决这个问题,关键的见解是,在数组的已排序部分中,我们实际上不需要检查一个桶来知道它有什么颜色的鹅卵石,只要我们一直跟踪如何我们已经看到了许多不同颜色的鹅卵石。如果我们之前见过r红色鹅卵石、w白色鹅卵石和b蓝色鹅卵石,那么红色鹅卵石在桶中0到r-1,白色在r到@ 987654330@,蓝色的在桶中r+w 到r+w+b-1。所以我们可以找出在哪里插入一个新的鹅卵石,而不必在任何其他鹅卵石上实际调用color。
第二个改进:对swap(i,j) 的调用次数,第 1 部分。
下一个问题是,对于纯插入排序,插入一个鹅卵石意味着“移动”它之后的所有鹅卵石,这可能意味着大量的交换。 (例如,如果我们已经看到 10 个白色鹅卵石和 5 个蓝色鹅卵石,那么插入一个红色鹅卵石意味着“移动”15 个鹅卵石,这需要 15 次交换。)
要解决这个问题,关键在于我们实际上并不关心颜色组中鹅卵石的顺序;红色鹅卵石可以以任何顺序出现,其次是白色鹅卵石以任何顺序排列,然后是蓝色鹅卵石以任何顺序排列。因此,我们可以将单个 pebble 从颜色组的开头移动到它的结尾,而不是将大量鹅卵石移动一个位置。
例如,如果我们之前看到过r红色鹅卵石、w白色鹅卵石和b蓝色鹅卵石,并且我们需要插入一个新的红色鹅卵石,那么我们可以将其交换到@987654338位置@(给我们一个白色鹅卵石(如果w > 0)),然后将白色鹅卵石交换到位置r+w(给我们一个蓝色鹅卵石(如果b > 0))。
这使我们在最坏的情况下减少到 O(n) 次交换,而在平均情况下则低于 n 交换:一个红色卵石将需要两次交换(如果 w > 0 和b > 0),白色鹅卵石需要一次交换(如果 b > 0),蓝色鹅卵石需要零次交换。
第三项改进:对swap(i,j) 的调用次数,第 2 部分。
但这仍然给我们留下可能过多的交换:如果红色鹅卵石多于蓝色鹅卵石,那么我们(通常)最终会得到超过 n 交换。
要解决这个问题,我们需要最后一点洞察力,即我们知道蓝色鹅卵石最终会在哪里(因为我们不关心它们最终的顺序);因此,我们可以将蓝色鹅卵石放在数组的 end 之后,而不是在已经看到的白色鹅卵石结束后立即放置蓝色鹅卵石。这样我们就不需要不断地重新移动蓝色鹅卵石的范围;一旦放置了蓝色鹅卵石,它就可以原地不动。
因此,例如,如果我们看到了r 红色鹅卵石、w 白色鹅卵石和b 蓝色鹅卵石,那么红色鹅卵石应该位于0 到r-1 的位置,白色鹅卵石应位于r 至r+w-1 的位置,蓝色鹅卵石应位于n-b 至n-1 的位置。从r+w 到n-b-1 的位置是尚未排序的范围。
所以,我们的最终算法如下所示:
- 将
r、w和b初始化为0。
- 将
i初始化为0。
- 而
i < n - b:
- 检查
color(i):
- 如果是 RED,则
swap(i, r) 并递增 r 和 i。
- 如果是 WHITE,则只增加
w 和 i。
- 如果是蓝色,则
swap(i, n-b-1) 并递增b。 不要增加i(因为i位置的鹅卵石是我们刚刚从数组末尾附近拉出的鹅卵石,我们还没有检查过)。
(请注意,通过仔细检查被交换的两个位置是否实际上不同,可以消除一些交换;但是,这不是必需的。无论哪种方式,我们都处于 n 个交换之下。)
现在,如果我们从不同的排序开始,而不是插入排序呢?某些种类仍然会带我们朝着正确的方向前进;例如,如果我们从桶排序(或只有一个数字 0-2 的基数排序)开始,我们仍然可以进行增量修复以结束上述算法。其他种类的方法效果不会那么好,但仍然可能会带来有用的见解,一旦我们走上更好的道路,这些见解就会有所帮助。所以尝试不同的事情并保持开放的心态很重要。您探索问题的次数越多,获得的见解就越多,即使您偶尔会走错路,需要退出并尝试新事物。