マージソートの視覚化


30

マージソートは、指定されたリストを半分に分割し、両方の小さなリストを再帰的にソートし、それらを1つのソート済みリストにマージして戻すソートアルゴリズムです。再帰の基本ケースはシングルトンリストに到達しているため、さらに分割することはできませんが、定義ごとに既にソートされています。

リスト上のアルゴリズムの実行は[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

最後に、リストを2つの小さなリストに分割する方法は、結果の両方のリストの長さがせいぜい1つずつ異なる限り、あなた次第です。その手段の代わりに、分割[3,2,4,3,7][3,2,4]して[3,7]、あなたはまた、偶数と奇数のインデックス(の要素を取ることによって分割することができ[3,4,7]かつ[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
flawr

5
ゴルフのアイデア:前半で各サブリストを並べ替え、順序を逆にすることで、結果の下半分を生成できます。
ジョンファンミン

2
@Arnauld [[1,2],[3],[4,5],[6]]マージソートは再帰的に機能するため、実際にはステージは正しいソリューションです。我々が開始した場合それはある[1,2,3,4,5,6]とに分割[1,2,3]して[4,5,6]、それらが最終段階にマージされるまで、これらのリストは、独立して処理されています。
ライコニ

2
@ l4m2 OK、最後に答えを試してみてください:1.整数もサポートする必要があるため、区切り文字が必要です。2.これは、上記の私のコメントと同じ理由で無効です。我々はに分割した場合[3][2,1]、我々はマージすることはできませんので、それらは、異なる枝にある[3][2]後の[2,1]分割はにある[2][1]
ライコニ

1
実際、その後の文は私の質問に正確に答えます。見逃してすみません。:P
ズガルブ

回答:


8

ハスケル137の 128 127 125 121 109 106バイト

nimiのおかげで(-2)+(-4)=(-6)バイト!リスト内のすべてのステップを収集するように変更すると(これも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

オンラインでお試しください!

リストを奇数と偶数に配置された要素に分割することは、2つの連続した半分に分割するよりも短いコードですlength

リストを「印刷」し、実際に分割が行われた場合は分割リストで再帰し(x >>= h)、ソートされたリストを「印刷」します。1つの入力リストから始めます。空でない入力を想定しています。そして、実際の印刷の代わりに、それらをリストに集めるだけです。

によって生成されたリストは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そして3回pオンラインでお試しください!
nimi

@nimi素晴らしい、また、ありがとう!今では本当にゴルフのように見えます。:)
ウィルネス

関数内で印刷する代わりgに、リスト内のすべてのステップを収集して返すことができます。オンラインでお試しください!
nimi

3
「視覚化」の適切な定義があるとは思わない。より一般的な課題は、リストの「出力」を要求することであり、デフォルトでは、関数の戻り値を介してこれを行うことができます。他の回答、例えば12は、あまりにも、それをこのように行います。-私の提案はそれほど大きく異なるとは思わない。中間リストを印刷するのではなく単に収集するだけだ。でそれを編集すること自由に感じなさい。
nimi

3
g x|y<-x>>=h=x:[z|x/=y,z<-g y++[sort<$>x]]さらにバイトを節約します。
ライコニ

7

タングステン言語(Mathematicaの)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]&

入力では、List2つ以上の整数を含むすべてのsを半分に分割します。最初のサブリストには奇数インデックスの要素(1からインデックス)があり、2番目のサブリストには偶数インデックスの要素があります。

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

何も変更されなくなるまで繰り返します(つまり、すべてのサブリストは長さ1です)。すべての中間結果を保持します。これを(Listすべてのステップの)に保存しuます。

Reverse@Most@u

の最後の要素を削除し、u逆にします。

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

上記の結果から、整数のリストのすべての出現をソートします。



4

木炭145の 133 123 122バイト

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

オンラインでお試しください!リンクは、コードの詳細バージョンです。まだ炭のバグを回避する必要があります...編集:Map貧乏人の配列理解としてdouble を使用して5バイトを保存しました。Pop配列の二重反復を使用して4バイトを節約しました。の代わりに連結を使用して3バイトを保存しましたPushwhileチャコールバグも回避するゴルファーの状態を考え出すことで10バイトを節約しました。Charcoalに実際にフィルター演算子があることを発見して、1バイトを節約しました。説明:

≔⟦⪪S ⟧θ

入力をスペースで分割し、結果を外部配列にラップして、保存しqます。

W⊖L§θ⁰«

の最初の要素にq複数の要素がある間に繰り返します。(qリストが2つに分割されるため、の最初の要素には常に最も多くの要素があります。)

⟦⪫Eθ⪫κ ¦|⟧

q結合されたスペースの要素と垂直線を印刷します。(配列は結果を独自の行に出力します。同じバイトカウントでこれを達成する方法は他にもあります。)

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

の各要素を複製してリストを作成し、qそのリストにマップして各リストの半分を取得し(代替要素アプローチを使用)、結果をに保存しqます。

⟦⪫ΦEθ⪫ι ι|⟧

q結合されたスペースの要素と垂直線を印刷します。実際、この時点での要素はq空または単一要素のリストであるため、それらを結合すると、空の文字列または要素に変換されます。空の文字列は不要な末尾の行を追加するため、除外されます。ただし、平坦化演算子はゴルファーになります(のようなもの⟦⪫Σθ|⟧)。

W⊖Lθ«

q複数の要素がある間に繰り返します。(次のコードには、偶数個の要素が必要です。)

≔⮌θη≔⟦⟧θ

にコピーqしますhが、逆順にして(以下を参照)、q空のリストにリセットします。

Wη«

hが空になるまで繰り返します。

≔⊟ηε

の次の要素をhに抽出しeます。(Pop最後から抜粋しているので、逆にする必要がありますq。)

≔⟦⟧ζ

z空のリストに初期化します。

F⊟η«

の次の要素の要素をループしhます。

≔εδ≔⟦⟧ε

空のリストにコピーedてリセットeします。

Fδ

の要素をループしdます。

⊞⎇‹IμIλζεμ

それらを押しzたりe、彼らが次の要素の現在の要素よりも小さいかどうかに応じてh

⊞ζλ»

の次の要素の現在の要素をにプッシュhzます。

⊞θ⁺ζε»

z残っている要素と連結し、eそれをにプッシュしqます。これにより、の2つの要素のマージが完了しhます。

⟦⪫Eθ⪫κ ¦|

q結合されたスペースの要素と垂直線を印刷します。


待って。別のバグがありますか?:/
ASCIIのみ

@ASCIIのみいいえ、これはwhile (...Map(...)...)既に説明したバグでした。
ニール

3

パイソン2145の 144バイト

ジョン・ファン・ミンのコメントからのアイデア-1バイトジョナサン・フレッシュの
おかげ

p=input()
l=[[p]]
i=0
while 2**i<len(p):l[i+1:i+1]=[sum([[x[1::2],x[::2]][len(x)<2:]for x in l[i]],[]),map(sorted,l[i])];i+=1
for s in l:print s

オンラインでお試しください!


代わりに使用できるかもしれ<2==1ません、私は完全にはわかりません。
ジョナサンフレッチ

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バイトに結び付けられた3つの答えがあった点がありましたか?
ニール

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.

ビルトイン½は配列を受け取り、中央で分割します。長さが奇数の場合、最初の部分は1要素分長くなります。シングルトン配列は[a]結果として[[a],[]]空の配列になり[]ます[[],[]]ので、適用する前に空の配列を削除する必要がありますU


1

スタックス116(÷3>) 38 29 バイトCP437

@recursiveによるコメントごとに-9バイト。これで、入力は、ソートされる数字の配列のみを要素とするシングルトンとして与えられます。

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

オンラインでお試しください!

35バイトのアンパックバージョン:

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

説明

コードは2つの部分に分割できます。最初の部分は分割を視覚化し、2番目の部分はマージを視覚化します。

分割を視覚化するためのコード:

{                      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+= スタックの最上位がスカラーか配列かを確認するために、コードで3回使用されます。

{{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

分割とマージをそれぞれ実行する他の2つのブロックがあります。それらは冗長すぎるため、説明する必要はありません(この投稿の履歴バージョンにはもう少し情報がありますが、あまり期待しないでください)。


非常に素晴らしい改善。まだ完全にはわかりませんがcH!、の代わりに使用できると思いますcH%!
再帰的

{Nd}Mに置き換えることTもできます。
再帰的

私はさらにいくつかの適応を行いました。 staxlang.xyz/…Husk の答えは、配列内の配列として入力を受け取るため、それは正当なものだと思います。
再帰的

2 ASCII文字より短いソリューションを見つけましたが、配列転置のバグを発見しました。具体的には、配列の行を変更します。1.0.4のバックログに追加します
再帰

OK。更新を楽しみにしています。
ウェイジュン周
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.