Visualizza Merge Sort


30

Unisci ordinamento è un algoritmo di ordinamento che funziona suddividendo un determinato elenco a metà, ordinando ricorsivamente entrambi gli elenchi più piccoli e riunendoli nuovamente in un elenco ordinato. Il caso base della ricorsione sta arrivando a un elenco singleton, che non può essere ulteriormente diviso ma è già per definizione ordinato.

L'esecuzione dell'algoritmo nell'elenco [1,7,6,3,3,2,5]può essere visualizzata nel modo seguente:

       [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]

L'obiettivo

Scrivi un programma o una funzione che accetta un elenco di numeri interi in modo ragionevole come input e visualizza le diverse partizioni di questo elenco mentre viene ordinato da un algoritmo di ordinamento di tipo merge. Ciò significa che non devi produrre un grafico come sopra, ma solo gli elenchi vanno bene:

[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]

Inoltre, qualsiasi ragionevole notazione di elenco va bene, quindi anche un risultato valido sarebbe:

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

Infine, il modo di dividere un elenco in due elenchi più piccoli dipende da te purché la lunghezza di entrambi gli elenchi risultanti differisca al massimo di uno. Ciò significa che invece di dividere [3,2,4,3,7]in [3,2,4]e [3,7], puoi anche dividere prendendo elementi in indici pari e dispari ( [3,4,7]e [2,3]) o anche randomizzare la divisione ogni volta.

Questo è , quindi vince il codice più breve in qualsiasi lingua misurato in byte.

Casi test

Come notato sopra, il formato attuale e il modo di dividere gli elenchi a metà dipende da te.

[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 È possibile utilizzare un altro algoritmo di ordinamento o una funzione di ordinamento integrata per eseguire l'ordinamento ...
flawr

5
Qualche idea sul golf: la metà inferiore del risultato può essere generata ordinando ciascun elenco secondario nella prima metà e invertendo l'ordine.
JungHwan Min,

2
@Arnauld Lo [[1,2],[3],[4,5],[6]]stage è in realtà la soluzione corretta, poiché l'ordinamento per merge funziona in modo ricorsivo. Cioè se iniziamo con [1,2,3,4,5,6]e lo dividiamo in [1,2,3]e [4,5,6], quindi quegli elenchi vengono elaborati indipendentemente fino a quando non vengono uniti nel passaggio finale.
Laikoni,

2
@ l4m2 Ok, prova finale per una risposta: 1. hai bisogno di delimitatori perché dovrebbero essere supportati anche numeri interi> 9. 2. Questo non è valido per lo stesso motivo indicato nel mio commento sopra. Se ci dividiamo in [3]e [2,1], allora quelli sono su rami diversi, quindi non possiamo unirci [3]e [2]dopo [2,1]viene diviso in [2]e [1].
Laikoni,

1
In effetti la frase successiva risponde esattamente alla mia domanda. Scusa se mi manca. : P
Zgarb,

Risposte:


8

Haskell , 137 128 127 125 121 109 106 byte

(-2) + (- 4) = (- 6) byte grazie a nimi ! Modificandolo per raccogliere tutti i passaggi in un elenco (sempre a causa di nimi) si salvano altri 12 byte.

Altri 3 byte dovuti a Laikoni , con rilegatura della protezione del modello e un uso intelligente della comprensione dell'elenco per codificare la protezione.

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

Provalo online!

Dividere gli elenchi negli elementi dispari e posizionati in modo uniforme è un codice più breve rispetto alle due metà sequenziali, perché ci viene risparmiato dover misurare length, quindi.

Funziona "stampando" gli elenchi, quindi ricorrendo agli elenchi divisi ( x >>= h) se effettivamente è stata effettuata una suddivisione e "stampando" gli elenchi ordinati; a partire da un elenco di input; presupponendo un input non vuoto. E invece della stampa effettiva, semplicemente raccogliendoli in un elenco.

L'elenco prodotto da g[[6,5..1]], stampato riga per riga, è:

[[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=printe tre volte p. Provalo online!
nimi,

@nimi ottimo, ancora una volta, e molte grazie! ora sembra davvero golfato . :)
Will Ness il

Invece di stampare all'interno della funzione gè possibile raccogliere tutti i passaggi in un elenco e restituirlo. Provalo online!
nimi,

3
Non penso che abbiamo una definizione corretta di "visualizzazione". Più in generale, le sfide richiedono di "emettere" le liste e per impostazione predefinita ciò può essere fatto tramite un valore di ritorno di una funzione . Anche altre risposte, ad esempio 1 , 2 , fanno così. - Non credo che il mio suggerimento sia molto diverso, raccoglie solo le liste intermedie invece di stamparle. Sentiti libero di modificarlo.
nimi

3
g x|y<-x>>=h=x:[z|x/=y,z<-g y++[sort<$>x]]salva qualche altro byte.
Laikoni,

7

Wolfram Language (Mathematica) , 146 127 111 102 byte

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

Provalo online!

Restituisce un Listche contiene i passaggi.

Spiegazione

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

In un input, dividere a metà tutti gli Lists contenenti 2 o più numeri interi. Il primo elenco secondario ha gli elementi con indicizzazione dispari (1-indicizzati) e il secondo con gli elementi con indice pari.

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

Ripetere l'operazione fino a quando non cambia nulla (ovvero tutte le liste secondarie hanno lunghezza 1). Mantieni tutti i risultati intermedi. Conservare questo (il Listdi tutti i passaggi) in u.

Reverse@Most@u

Rilascia l'ultimo elemento di ue invertilo.

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

Dal risultato sopra, ordina tutte le occorrenze di un elenco di numeri interi.


6

Pulito , 228 206 168 157 140 121 104 byte

Costruisce l'elenco delle fasi dalle estremità verso l'interno, utilizzando il fatto che l' nelemento -th dalla fine è la versione ordinata ndell'elemento -th dall'inizio.

Idea dal commento di JungHwan Min

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

Provalo online!


4

Carbone , 145 133 123 122 byte

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

Provalo online! Il collegamento è alla versione dettagliata del codice. Dobbiamo ancora aggirare i bug del carbone ... Modifica: Salva 5 byte usando un doppio Mapcome comprensione dell'array di un uomo povero. Salvato 4 byte usando Popper raddoppiare l'iterazione su un array. Salvato 3 byte utilizzando la concatenazione anziché Push. Hai salvato 10 byte presentando una whilecondizione più golfistica che evita anche un bug di carbone. Salvato 1 byte scoprendo che Carbone ha effettivamente un operatore di filtro. Spiegazione:

≔⟦⪪S ⟧θ

Dividi l'input negli spazi, quindi avvolgi il risultato in un array esterno, salvandolo in q.

W⊖L§θ⁰«

Ripeti mentre il primo elemento di qha più di un elemento. (Il primo elemento di qha sempre il maggior numero di elementi a causa del modo in cui le liste sono divise in due.)

⟦⪫Eθ⪫κ ¦|⟧

Stampa gli elementi di qunione con spazi e linee verticali. (L'array fa sì che il risultato venga stampato sulla propria riga. Esistono altri modi per ottenere questo risultato con lo stesso conteggio byte.)

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

Crea un elenco duplicando ogni elemento di q, quindi mappalo su quell'elenco e prendi metà di ogni elenco (usando l'approccio con elemento alternativo), salvando nuovamente il risultato q.

⟦⪫ΦEθ⪫ι ι|⟧

Stampa gli elementi di qunione con spazi e linee verticali. In realtà, a questo punto gli elementi di qsono elenchi vuoti o a singolo elemento, quindi unirli li converte in stringhe vuote o nei loro elementi. Le stringhe vuote aggiungerebbero inutili righe finali in modo che vengano filtrate. Un operatore appiattito sarebbe stato più golfista (qualcosa del genere ⟦⪫Σθ|⟧).

W⊖Lθ«

Ripeti mentre qha più di un elemento. (Il codice seguente richiede un numero pari di elementi.)

≔⮌θη≔⟦⟧θ

Copia qin h, ma invertito (vedi sotto) e ripristina qin un elenco vuoto.

Wη«

Ripeti fino a quando non hè vuoto.

≔⊟ηε

Estrai l'elemento successivo di hin e. ( Popestratti dalla fine, motivo per cui devo invertire q.)

≔⟦⟧ζ

Inizializza zin un elenco vuoto.

F⊟η«

Passa sopra gli elementi del prossimo elemento di h.

≔εδ≔⟦⟧ε

Copia ein de reimposta ein un elenco vuoto.

Fδ

Passa sopra gli elementi di d.

⊞⎇‹IμIλζεμ

Spingili zo a eseconda che siano più piccoli dell'elemento corrente dell'elemento successivo di h.

⊞ζλ»

Spingere l'elemento corrente dell'elemento successivo di ha z.

⊞θ⁺ζε»

Concatena zcon gli eventuali elementi rimanenti ee spingilo verso q. Questo completa l'unione di due elementi di h.

⟦⪫Eθ⪫κ ¦|

Stampa gli elementi di qunione con spazi e linee verticali.


Un attimo. C'è un altro bug? : /
ASCII,

@ Solo ASCII No, questo era il while (...Map(...)...)bug di cui ti avevo già parlato.
Neil,


2

JavaScript (ES6), 145 byte

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`|`:``)

Accetta input come array all'interno di un array, ad es f([[6,5,4,3,2,1]]). Funziona generando la prima e l'ultima riga dell'output, quindi suddividendola e richiamando se stessa, fino a quando ogni sub-array ha lunghezza 1. Ecco una dimostrazione di base di come funziona:

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
Quindi, c'era un punto in cui c'erano tre risposte legate a 145 byte?
Neil,

2

Buccia , 14 byte

S+ȯ†O↔hUmfL¡ṁ½

Accetta un array contenente un singolo array. Provalo online!

Spiegazione

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.

Il built-in ½prende un array e lo divide al centro. Se la sua lunghezza è dispari, la prima parte è più lunga di un elemento. Si [a]ottiene un array singleton [[a],[]]e un array vuoto [][[],[]], quindi è necessario rimuovere gli array vuoti prima dell'applicazione U.


1

Stax , 116 (÷ 3>) 38 29 byte CP437

-9 byte per commento di @recursive. Ora l'input è dato come un singleton il cui unico elemento è una matrice dei numeri da ordinare.

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

Provalo online!

Versione non compressa con 35 byte:

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

Spiegazione

Il codice può essere diviso in due parti. La prima parte visualizza la divisione e la seconda visualizza l'unione.

Codice per visualizzare la divisione:

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

Il codice per visualizzare l'unione:

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

Vecchia versione, in realtà costruendo la struttura dell'elenco nidificato.

{{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+= viene utilizzato tre volte nel codice per verificare se la parte superiore dello stack è uno scalare o un array.

{{cc0+=!{x!}Mm',*:}}Xcrea un blocco che si chiama ricorsivamente per generare correttamente una matrice nidificata di numeri. (L'output predefinito in Stax vettorializza un array nidificato prima della stampa).

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

Esistono altri due blocchi che eseguono rispettivamente la divisione e l'unione. Sono troppo dettagliati e non mi interessa spiegare (ci sono un po 'più di informazioni nella versione storica di questo post ma non mi aspetto troppo).


Miglioramento molto bello. Non lo capisco ancora del tutto, ma penso che cH!possa essere usato al posto di cH%!.
ricorsivo il

{Nd}Mpuò essere sostituito Tanche da .
ricorsivo il

Ho fatto altri adattamenti. staxlang.xyz/… La risposta di Husk accetta input come array in un array, quindi suppongo sia legale.
ricorsivo il

Ho trovato una soluzione che dovrebbe essere più corta di 2 caratteri ASCII, ma ho scoperto un bug nella trasposizione dell'array. In particolare, muta le righe dell'array. Lo aggiungerò al backlog per 1.0.4
ricorsivo il

OK. Attendo con ansia l'aggiornamento.
Weijun Zhou,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.