Візуалізація сортування об'єднань


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]і [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
Деяка ідея з гольфу: нижня половина результату може бути сформована шляхом сортування кожного списку в першій половині та зміни порядку.
JungHwan Min

2
@Arnauld [[1,2],[3],[4,5],[6]]Етап насправді є правильним рішенням, оскільки сортування злиття працює рекурсивно. Тобто, якщо ми почнемо з [1,2,3,4,5,6]і розділимо їх на, [1,2,3]і [4,5,6]тоді ці списки обробляються незалежно, поки вони не будуть об'єднані на останньому кроці.
Лайконі

2
@ l4m2 Добре, остаточна спроба відповіді: 1. Вам потрібні роздільники, тому що також слід підтримувати цілі числа> 9. 2. Це не дійсно з тієї ж причини, що вказана в моєму коментарі вище. Якщо ми розділимось на [3]і [2,1], то вони знаходяться на різних гілках, тому ми не можемо злитися, [3]а [2]після [2,1]розщеплені на [2]і [1].
Лайконі

1
Насправді речення після цього точно відповідає моєму питанню. Вибачте, що пропустили це. : P
Zgarb

Відповіді:


8

Хаскелл , 137 128 127 125 121 109 106 байт

(-2) + (- 4) = (- 6) байт завдяки німі ! Змінивши його, щоб зібрати всі кроки у списку (знову ж таки завдяки німі), економиться ще 12 байт.

Ще 3 байти завдяки Laikoni , з прив'язкою захисного шаблону та розумним використанням розуміння списку для кодування захищаючого пристрою .

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 чудово, знову ж таки, і велике спасибі! тепер він справді виглядає гольф . :)
Буде Несс

Замість друку в межах функції gви можете зібрати всі кроки у списку та повернути його. Спробуйте в Інтернеті!
німі

3
Я не думаю, що у нас є правильне визначення поняття "візуалізація". Більш загальне завдання викликає "виведення" списків, і за нашими типовими значеннями це можна зробити за допомогою зворотного значення функції . Інші відповіді, наприклад, 1 , 2, роблять це і так. - Я не думаю, що моя пропозиція настільки різна, вона просто збирає проміжні списки замість того, щоб друкувати їх. Не соромтеся відредагувати його.
німі

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

Спробуйте в Інтернеті!

Повертає a List що містить кроки.

Пояснення

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

На вході розділіть навпіл усі Lists, що містять 2 або більше цілих чисел. Перший підпис має елементи непарної індексації (1-індексований), а другий - парні.

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

Повторіть це, поки нічого не зміниться (тобто всі списки мають довжину-1). Зберігайте всі проміжні результати. Зберігайте це (у Listвсіх кроках) у u.

Reverse@Most@u

Відкиньте останній елемент uта поверніть його назад.

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

З наведеного вище результату сортуйте всі входи списку цілих чисел.


6

Чисто , 228 206 168 157 140 121 104 байт

Побудує список етапів від кінців всередину, використовуючи той факт, що n-м елементом від кінця є відсортована версія n-го елемента з початку.

Ідея з коментаря 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]]

Спробуйте в Інтернеті!


4

Вугілля , 145 133 123 122 байт

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

Спробуйте в Інтернеті! Посилання на багатослівну версію коду. Ще потрібно попрацювати над помилками деревного вугілля ... Редагувати: Збережено 5 байтів, використовуючи дубль Mapяк розуміння масиву бідних людей. Збережено 4 байти, використовуючи Popдля подвоєння ітерації масив. Збережено 3 байти, використовуючи конкатенацію замість Push. Збережено 10 байт, придумуючи whileумови для гольфіста, що також дозволяє уникнути помилки з вугіллям. Збережено 1 байт, виявивши, що вугілля дійсно має оператор фільтру. Пояснення:

≔⟦⪪S ⟧θ

Розділіть введення на пробіли, а потім оберніть результат у зовнішній масив, зберігаючи це в q.

W⊖L§θ⁰«

Повторіть, коли перший елемент qмає більше одного елемента. (Перший елемент qзавжди має найбільше елементів через те, як списки діляться на два.)

⟦⪫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.

≔εδ≔⟦⟧ε

Копіювати eв dі скинути eв порожній список.

Fδ

Петля над елементами d.

⊞⎇‹IμIλζεμ

Натисніть їх на zабо eзалежно від того, чи вони менші за поточний елемент наступного елемента h.

⊞ζλ»

Натисніть на поточний елемент наступного елемента hдо z.

⊞θ⁺ζε»

Об’єднайтеся zз будь-якими елементами, що залишилися, eі натисніть на q. Цим завершується злиття двох елементів h.

⟦⪫Eθ⪫κ ¦|

Друкуйте елементи qз'єднаних пробілами та вертикальними лініями.


Зачекайся. Є ще помилка? : /
лише для ASCII

@ ASCII-тільки Ні. Це була while (...Map(...)...)помилка, про яку я вам уже розповідав.
Ніл

3

Python 2 , 145 144 байт

Ідея з коментаря JungHwan Min
-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 байтах було приписано три відповіді?
Ніл

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

Stax , 116 (÷ 3>) 38 29 байт CP437

-9 байт за коментар від @recursive. Тепер введення подається як синглтон, єдиним елементом якого є масив чисел, які слід сортувати.

ƒ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
рекурсивний

ДОБРЕ. Я з нетерпінням чекаю на оновлення.
Вейджун Чжоу
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.