mergesort初学者问题

时间:2010-02-08 20:04:02

标签: algorithm

我现在对Mergesort算法有疑问。因为在原始算法中,要排序的列表被转移到两个子列表中并递归排序。 现在我想把lengh n列表分成3个lengh n / 3子列表,然后递归排序这三个子列表然后合并? 我只是简单地修改原始算法,将everwhere 2替换为3,想知道这是否有意义。

如何让它更通用?我们可以将列表分成K个子列表并对它们进行排序和组合吗?

感谢您与我分享您的想法。

4 个答案:

答案 0 :(得分:4)

该方法的问题在于合并3个列表比2个列表更难。使用2个列表,您只需进行一次比较(当前元素),而使用3个列表时,您必须至少进行两次比较(总是为n-1)。

合并排序的力量是合并。因此,您的想法将无效。对于并行排序,还有其他专门的算法。

答案 1 :(得分:4)

如果你有n(n> 2)个列表,那么你需要在每个合并步骤中进行(n-1)次比较。但是,实施变得更加复杂。

假设您有三个列表list[0..2],并且为了简单起见假设您正在合并它们并且它们仍然是非空的(即您没有运行到任何列表的末尾)。此外,为简单起见,假设所有元素都是不同的,即当您比较两个元素时,它们永远不会相同。然后你有六个可能的“状态”,它们对应于列表中第一个元素的递增顺序中三个列表的六个排列,即如果

list[0] = [5, 7, 11, 15]
list[1] = [3, 4, 20, 21]
list[2] = [9, 10, 12, 19]

然后列表的相应排列是[1,0,2],即list[1]具有最少的前元素,而list[2]具有最大的前元素。

当您现在从list[1]弹出下一个元素(4)时,您已经知道list[0].front< list[2].front基于您所在的州[1,0,2]。所以现在你需要进行1或2次比较:

if (list[1].front < list[0].front) // (A)
   --> move to state [1, 0, 2], next to pop is list[1]
else if (list[1].front < list[2].front)
   --> move to state[0, 1, 2], next to pop is list[0]
else
   --> move state[0, 2, 1], next to pop is list[0]

假设某种一致性,比较(A)返回true的概率,即你删除前一个元素的列表中的下一个元素小于另外两个列表中的最小元素,是1 / 3,所以你平均(1/3 x 1 + 2/3 x 2)= 5/3比较而不是2(这将是n-1)。

这显然比每次插入正常mergesort的2/3比较更糟糕,每个弹出元素只需要1个比较。

通过考虑部分有序状态,我们可以获得更好的结果。可以进行三种不同的比较(列表[0] - 列表[1],列表[1] - 列表[2]和列表[0] - 列表[2])。如果我们允许使用“不知道”(?)来扩充已知结果(&lt;,&gt;),则存在以下可能的状态:

0/1  1/2  0/2
  <    <    <   (total order) [0,1,2]
  <    ?    <   (0 is least but don't know if 1 < 2) [0,1,2] [0,2,1]
  <    ?    ?   (0 is < 1, but 2 can't be positioned) [2,0,1] [0,2,1] [0,1,2]
  ?    ?    ?   (no information about ordering) (all six permutations)

然后关于排列和交换的所有变体&lt;对于&gt;在矩阵的不同地方。

现在,如果处于(&lt;,&lt;,&lt;)状态(假设为[0,1,2])并且您从列表中读取下一个元素,则从中弹出前一个元素,有两种情况:要么(1)你得到的元素低于列表[1]上的元素,在这种情况下,你在一次比较中回到状态[0,1,2];或者你得到的元素高于列表[1]上的元素。在这种情况下,您可以输出list [1] next,并且您已输入(&lt;,?,&lt;)状态:您知道list [1]具有最少的前元素但现在不知道list [0 ]或列表[2]是下一个。

现在处于(&lt;,?,&lt;)状态,您从列表[1]中读取了一个新元素,您可以使用1 +(1/3 + 4/3)= 1 5/3比较来查找所有列表的实际排列,然后返回到(&lt;,&lt;,&lt;)状态。因此,这两次推动的顺序花费了2 5/3比较,平均每个插入1 5/6 = 11/6;但是,由于你可以在同一个列表的序列中插入两个最低元素,平均成本甚至更低,与之前相同的参数(1/3 + 2/3 x 11/6)= 6 / 18 + 22/18 = 1 + 5/9,比每次插入5/9比较的原始mergesort差,但略高于上面的2/3。

为了完整性,这里是算法(显示的片段):

state_1_lt_2: /* known list[1].front < list[2].front */
  if (list[0].front < list[1].front):
    merge_from(0)
    goto state_1_lt_2 /* 1 insert 1 comp prob 1/3 */
  else
    merge_from(1)
    if (list[0].front < list[1].front)
      if (list[1].front < list[2].front)
        merge_from(0)
        goto state_1_lt_2 /* 2 inserts 3 comps prob 2/3*1/2*1/3 = 1/9 */
      else if (list[0].front < list[2].front)
        merge_from(0)
        goto state_2_lt_1 /* 2 inserts 4 comps prob 2/3*1/2*2/3*1/2 = 1/9 */
      else
        merge_from(2)
        goto state_0_lt_1 /* 2 inserts 4 comps prob 1/9 */
    else if (list[2].front < list[1].front)
      merge_from(2)
      goto state_1_lt_0 /* 2 inserts 3 comps 2/3 x 1/2 x 1/3 = 1/9 */
    else if (list[2].front < list[0].front)
      merge_from(1)
      goto state_2_lt_0 /* 2 inserts 4 comps prob 1/9 */
    else
      merge_from(1)
      goto state_0_lt_2 /* 2 inserts 4 comps prob 1/9 */

这总计为每次插入

的预期比较次数
1/3 x 1 + 4/9 x (4/2) + 2/9 x (3/2) = 6/18 + 16/18 + 6/18 = 30/18 = 1 5/9.

答案 2 :(得分:3)

合并算法适用于两个列表。除非你以某种方式调整合并算法以同时处理三个列表,以比将列表1与列表2合并“更好”的方式,然后使用列表3(实际上是两个合并操作),执行三分区方法不会给出更好的结果。

答案 3 :(得分:1)

如果合并排序n元素通常的方式有Log(n)/ Log(2)* 1 * n比较,但如果你用k而不是2分割,则有aprox Log(n)/ Log(k)合并和ceil(log2(k!))比较每次合并。这意味着通过k分裂你将得到

nc=Log(2)/Log(k)*ceil(log2(k!))

对于k = 3增加1.26
对于k = 13 - > nc = 9.188(13个数字不能在少于34个比较中排序)