可视化合并排序


30

合并排序是一种排序算法,其工作原理是将给定列表分为两半,对两个较小的列表进行递归排序,然后将它们合并回一个排序的列表。递归的基本情况是到达一个单例列表,该列表无法进一步拆分,但已按定义进行了排序。

[1,7,6,3,3,2,5]可以通过以下方式可视化列表中算法的执行:

       [1,7,6,3,3,2,5]
       /             \      split
   [1,7,6,3]       [3,2,5]
    /     \        /    \   split
 [1,7]   [6,3]   [3,2]  [5]
 /   \   /   \   /   \   |  split
[1] [7] [6] [3] [3] [2] [5]
 \   /   \   /   \   /   |  merge
 [1,7]   [3,6]   [2,3]  [5]
    \     /         \   /   merge
   [1,3,6,7]       [2,3,5]
       \             /      merge
       [1,2,3,3,5,6,7]

任务

编写一个程序或函数,以任何合理的方式将整数列表作为输入,并可视化此列表的不同分区,同时通过合并排序算法对其进行排序。这意味着您不必输出上述图形,而只需列出即可:

[1,7,6,3,3,2,5]
[1,7,6,3][3,2,5]
[1,7][6,3][3,2][5]
[1][7][6][3][3][2][5]
[1,7][3,6][2,3][5]
[1,3,6,7][2,3,5]
[1,2,3,3,5,6,7]

此外,任何合理的列表符号都可以,因此以下内容也是有效的输出:

1 7 6 3 3 2 5
1 7 6 3|3 2 5
1 7|6 3|3 2|5
1|7|6|3|3|2|5
1 7|3 6|2 3|5
1 3 6 7|2 3 5
1 2 3 3 5 6 7

最后,只要两个结果列表的长度最多相差一个,就可以将列表分为两个较小的列表。这意味着除了拆分[3,2,4,3,7][3,2,4]and之外[3,7],您还可以通过采用偶数和奇数索引([3,4,7]and [2,3])的元素进行拆分,甚至每次都随机化拆分。

这是,因此以字节为单位的任何语言中最短的代码都会获胜。

测试用例

如上所述,实际格式和将列表分为两半的方法取决于您。

[10,2]
[10][2]
[2,10]

[4,17,1,32]
[4,17][1,32]
[4][17][1][32]
[4,17][1,32]
[1,4,17,32]

[6,5,4,3,2,1]
[6,5,4][3,2,1]
[6,5][4][3,2][1]
[6][5][4][3][2][1]
[5,6][4][2,3][1] <- Important: This step cannot be [5,6][3,4][1,2], because 3 and 4 are on different branches in the the tree
[4,5,6][1,2,3]
[1,2,3,4,5,6]

5
@dylnan您可以使用其他排序算法或内置的排序功能来进行排序...
fogr

5
打高尔夫球的一些想法:可以通过对前半部分的每个子列表进行排序并颠倒顺序来生成结果的下半部分。
JungHwan Min

2
@Arnauld [[1,2],[3],[4,5],[6]]阶段实际上是正确的解决方案,因为合并排序是递归的。也就是说,如果我们从开始[1,2,3,4,5,6]并将其拆分为[1,2,3][4,5,6],则这些列表将被独立处理,直到在最后一步中将它们合并。
Laikoni '17

2
@ l4m2好,最后一个答案:1.您需要定界符,因为还应该支持大于9的整数。2.这是无效的,原因与我上面的评论相同。如果我们拆分为[3][2,1],则它们位于不同的分支上,因此我们不能合并[3][2]之后[2,1]拆分为[2]and [1]
Laikoni '17

1
实际上,这之后的句子恰好回答了我的问题。很抱歉错过了。:P
Zgarb

Answers:


8

Haskell中137个 128 127 125 121 109 106字节

(-2)+(-4)=(-6)个字节感谢nimi!对其进行更改以收集列表中的所有步骤(同样由于nimi)可以节省12个字节。

由于Laikoni还有3个字节,具有模式保护绑定和巧妙地使用列表推导对保护进行编码。

import Data.List
g x|y<-x>>=h=x:[z|x/=y,z<-g y++[sort<$>x]]
h[a]=[[a]]
h x=foldr(\x[b,a]->[x:a,b])[[],[]]x

在线尝试!

将列表分为奇数个和偶数个元素比两个连续的两半要短,因为我们不必再测量length

通过作品“打印”列表,然后分割清单递归(x >>= h如果有实际上是任何分裂完成,以及“打印”排序名单; 从一个输入列表开始;假定为非空输入。而不是实际打印,只需将它们收集在列表中即可。

g[[6,5..1]]逐行打印的产生的列表是:

[[6,5,4,3,2,1]]
[[6,4,2],[5,3,1]]
[[6,2],[4],[5,1],[3]]
[[6],[2],[4],[5],[1],[3]]
[[2,6],[4],[1,5],[3]]
[[2,4,6],[1,3,5]]
[[1,2,3,4,5,6]]

1
... p=print三遍p在线尝试!
nimi

@nimi很好,再次,非常感谢!现在看起来真的很打高尔夫球。:)
威尔·内斯

g可以在列表中收集所有步骤并将其返回,而不是在函数中进行打印。在线尝试!
nimi

3
我认为我们对“可视化”没有正确的定义。更一般地说,挑战是要求“输出”列表,根据我们的默认设置,这可以通过函数返回值来完成。其他答案,如12做这种方式了。-我认为我的建议没有太大不同,它只是收集中间列表而不是打印它们。随时对其进行编辑
。– nimi

3
g x|y<-x>>=h=x:[z|x/=y,z<-g y++[sort<$>x]]保存更多的字节。
Laikoni '17

7

Wolfram语言(数学)146个 127 111 102字节

Join[u=Most[#/.a:{_,b=__Integer}:>TakeDrop[a,;;;;2]&~FixedPointList~#],Reverse@Most@u/.a:{b}:>Sort@a]&

在线尝试!

返回List包含步骤的。

说明

#/.a:{_,b=__Integer}:>TakeDrop[a,;;;;2]&

在输入中,将所有List包含2个或多个整数的s 分成两半。第一个子列表具有奇数索引元素(1索引),第二个子列表具有偶数索引元素。

u=Most[... &~FixedPointList~#]

重复该操作,直到没有任何变化(即所有子列表的长度均为1)。保留所有中间结果。将其(List所有步骤的总和)存储在中u

Reverse@Most@u

删除的最后一个元素u并将其反转。

... /.a:{b}:>Sort@a

根据以上结果,对所有出现的整数列表进行排序。


6

干净228个 206 168 157 140 121 104字节

使用n末尾的-th元素是末尾的-th元素的排序版本的事实,从末尾向内构建阶段列表n

郑焕敏评论的想法

import StdEnv
@u#(a,b)=splitAt(length u/2)u
=if(a>[])[[u]:[x++y\\y<- @b&x<- @a++ @a++ @a]][]++[[sort u]]

在线尝试!


4

木炭145个 133 123 122字节

≔⟦⪪S ⟧θW⊖L§θ⁰«⟦⪫Eθ⪫κ ¦|⟧≔EE⊗Lθ§θ÷λ²✂κ﹪λ²Lκ²θ»⟦⪫ΦEθ⪫ι ι|⟧W⊖Lθ«≔⮌θη≔⟦⟧θWη«≔⊟ηε≔⟦⟧ζF⊟η«≔εδ≔⟦⟧εFδ⊞⎇‹IμIλζεμ⊞ζλ»⊞θ⁺ζε»⟦⪫Eθ⪫κ ¦|

在线尝试!链接是详细版本的代码。仍然必须解决木炭错误...编辑:通过使用两倍Map作为穷人的数组理解,节省了5个字节。使用Pop两次遍历数组可节省4个字节。通过使用串联(而不是)节省了3个字节Push。通过提出高尔夫球手while条件来节省10个字节,这也避免了木炭虫。发现木炭确实具有过滤器运算符,从而节省了1个字节。说明:

≔⟦⪪S ⟧θ

将输入分割为空格,然后将结果包装在外部数组中,并将其保存为q

W⊖L§θ⁰«

在的第一个元素q具有多个元素时重复。(q由于列表分为两部分,因此第一个元素始终具有最多的元素。)

⟦⪫Eθ⪫κ ¦|⟧

打印q用空格和垂直线连接的元素。(该数组使结果在其自己的行上打印。对于相同的字节数,还有其他实现方法。)

≔EE⊗Lθ§θ÷λ²✂κ﹪λ²Lκ²θ»

通过复制的每个元素来创建列表q,然后映射到该列表并采用每个列表的一半(使用替代元素方法),然后将结果保存到中q

⟦⪫ΦEθ⪫ι ι|⟧

打印q用空格和垂直线连接的元素。实际上,此时的元素q是空列表或单元素列表,因此连接它们只是将它们转换为空字符串或它们的元素。空字符串将添加不必要的尾随行,因此将其过滤掉。扁平化的操作员本来应该是高尔夫球手(例如⟦⪫Σθ|⟧)。

W⊖Lθ«

重复一遍,q有多个元素。(以下代码需要偶数个元素。)

≔⮌θη≔⟦⟧θ

复制qh,但是反转(请参见下文),然后重置q为空白列表。

Wη«

重复直到h为空。

≔⊟ηε

将中的下一个元素提取h到中e。(Pop摘自最后,这就是为什么我必须扭转q。)

≔⟦⟧ζ

初始化 z为空列表。

F⊟η«

循环到的下一个元素h

≔εδ≔⟦⟧ε

复制ed并重置e为空列表。

Fδ

循环遍历的元素d

⊞⎇‹IμIλζεμ

将其推入ze取决于它们是否小于的下一个元素的当前元素h

⊞ζλ»

将的下一个元素的当前元素推hz

⊞θ⁺ζε»

z与剩余的任何元素串联,e然后将其推送到q。这样就完成了的两个元素的合并h

⟦⪫Eθ⪫κ ¦|

打印q用空格和垂直线连接的元素。


不挂断。还有另一个错误吗?:/
仅ASCII的

仅限@ASCII,不,这是while (...Map(...)...)我已经告诉过您的错误。
尼尔,


2

JavaScript(ES6),145个字节

f=a=>a.join`|`+(a[0][1]?`
${f([].concat(...a.map(b=>b[1]?[b.slice(0,c=-b.length/2),b.slice(c)]:[b])))}
`+a.map(b=>b.sort((x,y)=>x-y)).join`|`:``)

将输入作为数组内的一个数组,即f([[6,5,4,3,2,1]])。通过生成输出的第一行和最后一行,然后再次拆分并调用自身来工作,直到每个子数组的长度为1。以下是其工作原理的基本演示:

f([[6,5,4,3,2,1]]):
  6,5,4,3,2,1
  f([[6,5,4],[3,2,1]]):
    6,5,4|3,2,1
    f([[6,5],[4],[3,2],[1]]):
      6,5|4|3,2|1
      f([[6],[5],[4],[3],[2],[1]]):
        6|5|4|3|2|1
      end f
      5,6|4|2,3|1
    end f
    4,5,6|1,2,3
  end f
  1,2,3,4,5,6
end f

2
那么,是否有一个问题,在145个字节上绑定了三个答案?
尼尔

2

外壳,14个字节

S+ȯ†O↔hUmfL¡ṁ½

接受一个包含单个数组的数组。 在线尝试!

说明

S+ȯ†O↔hUmfL¡ṁ½  Implicit input, say A = [[4,17,32,1]].
           ¡    Iterate this function on A:
            ṁ½   Split each array in two, concatenate results: [[4,17],[32,1]]
                Result is [[[4,17,32,1]],
                           [[4,17],[32,1]],
                           [[4],[17],[32],[1]],
                           [[4],[],[17],[],[32],[],[1],[]],
                           ...
        mfL     Map filter by length, removing empty arrays.
                Result is [[[4,17,32,1]],
                           [[4,17],[32,1]],
                           [[4],[17],[32],[1]],
                           [[4],[17],[32],[1]],
                           ...
       U        Longest prefix of unique elements:
                       P = [[[4,17,32,1]],[[4,17],[32,1]],[[4],[17],[32],[1]]]
      h         Remove last element: [[[4,17,32,1]],[[4,17],[32,1]]]
     ↔          Reverse: [[[4,17],[32,1]],[[4,17,32,1]]]
   †O           Sort each inner array: [[[4,17],[1,32]],[[1,4,17,32]]]
S+ȯ             Concatenate to P:
                           [[[4,17,32,1]],
                            [[4,17],[32,1]],
                            [[4],[17],[32],[1]],
                            [[4,17],[1,32]],
                            [[1,4,17,32]]]
                Implicitly print.

内置½函数接受一个数组并将其在中间拆分。如果其长度为奇数,则第一部分将增加一个元素。[a]结果是单例数组,[[a],[]]而空数组[]给出了[[],[]],因此必须在应用之前删除空数组U


1

Stax116(÷3>) 38 29 字节 CP437

@recursive每个注释-9个字节。现在,输入以单例形式给出,其唯一元素是要排序的数字数组。

ƒ3s}óºE/ßB╢↕êb∩áαπüµrL╞¶è,te+

在线尝试!

35字节的解压缩版本:

{c{Jm'|*Pc{2Mm:f{fc{Dm$wW{{eoJm'|*P

说明

该代码可以分为两部分。第一部分可视化拆分,第二部分可视化合并。

可视化拆分的代码:

{                      w    Do the following while the block generates a true value
 c                          Copy current nested array for printing
  {Jm                       Use spaces to join elements in each part
     '|*                    And join different parts with vertical bar 
        P                   Pop and print

         c                  Copy current nested array for splitting
          {2Mm              Separate each element of the array to two smaller parts with almost the same size
                                That is, if the number of elements is even, partition it evenly.
                                Otherwise, the first part will have one more element than the second.
              :f            Flatten the array once
                {f          Remove elements that are empty arrays

                  c         Copy the result for checking 
                   {Dm$     Is the array solely composed of singletons?
                            If yes, ends the loop.

可视化合并的代码:

W              Execute the rest of the program until the stack is empty
 {{eoJm        For each part, sort by numeric value, then join with space
       '|*     Join the parts with vertical bar
          P    Pop and print the result

旧版本,实际上是在构建嵌套列表结构。

{{cc0+=!{x!}Mm',*:}}Xd;%v:2^^{;s~{c^;<~sc%v,*{2M{s^y!svsm}M}YZ!x!Q,dmU@e;%v:2^{;%v:2^-N~0{c;={scc0+=Cc%v!C:f{o}{scc0+=C{s^y!svsm}?}Y!cx!P,dcm

cc0+= 在代码中使用三次来检查堆栈的顶部是标量还是数组。

{{cc0+=!{x!}Mm',*:}}X构建一个块,该块递归地调用自身以正确输出嵌套的数字数组。(Stax中的默认输出在打印之前对嵌套数组进行矢量化处理)。

{                  }X    Store the code block in X
 {           m           Map each element in the list with block
  cc                     Make two copies of the element
    0+                   + 0. If the element is a scalar, nothing will change
                              If the element is an array, the element 0 will be appended
      =!{  }M            If the element has changed after the `0+` operation
                             Which means it is an array
         x!              Recursively execute the whole code block on the element

              ',*        Join the mapped elements with comma
                 :}      Bracerizes the final result

还有另外两个分别执行拆分和合并的块。它们太冗长,我不在乎解释(本文的历史版本中提供了更多信息,但不要期望太多)。


非常好的改进。我尚不完全了解,但我认为cH!可以代替cH%!
递归

{Nd}M也可以被替换T
递归

我做了一些改编。 staxlang.xyz/…Husk 答案将输入作为数组中的一个数组,所以我认为这是合法的。
递归

我找到了一个解决方案,该解决方案应该短2个ascii字符,但是我发现了数组转置中的错误。具体来说,它使数组行发生突变。我将其添加到1.0.4的积压中
递归

好。我期待更新。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.