Trực quan hóa sắp xếp hợp nhất


30

Hợp nhất sắp xếp là một thuật toán sắp xếp hoạt động bằng cách chia một nửa danh sách đã cho, sắp xếp đệ quy cả hai danh sách nhỏ hơn và hợp nhất chúng lại với nhau thành một danh sách được sắp xếp. Trường hợp cơ bản của đệ quy đang đến một danh sách đơn, không thể phân tách thêm nhưng theo định nghĩa đã được sắp xếp.

Việc thực hiện thuật toán trong danh sách [1,7,6,3,3,2,5]có thể được hiển thị theo cách sau:

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

Nhiệm vụ

Viết chương trình hoặc hàm lấy danh sách các số nguyên theo bất kỳ cách hợp lý nào làm đầu vào và trực quan hóa các phân vùng khác nhau của danh sách này trong khi được sắp xếp theo thuật toán sắp xếp hợp nhất. Điều này có nghĩa là bạn không phải xuất một biểu đồ như trên, nhưng chỉ các danh sách là ổn:

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

Hơn nữa, bất kỳ ký hiệu danh sách hợp lý nào cũng tốt, do đó, đây cũng sẽ là một đầu ra hợp lệ:

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

Cuối cùng, cách chia một danh sách thành hai danh sách nhỏ hơn tùy thuộc vào độ dài của cả hai danh sách kết quả khác nhau nhiều nhất là một. Điều đó có nghĩa là thay vì chia [3,2,4,3,7]thành [3,2,4][3,7], bạn cũng có thể phân chia bằng cách lấy các phần tử ở các chỉ số chẵn và lẻ ( [3,4,7][2,3]) hoặc thậm chí ngẫu nhiên phân tách mỗi lần.

Đây là , vì vậy mã ngắn nhất trong bất kỳ ngôn ngữ nào được đo bằng byte sẽ thắng.

Các trường hợp thử nghiệm

Như đã lưu ý ở trên, định dạng thực tế và cách chia danh sách thành một nửa là tùy thuộc vào bạn.

[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 Bạn có thể sử dụng thuật toán sắp xếp khác hoặc hàm sắp xếp sẵn để thực hiện sắp xếp ...
flawr

5
Một số ý tưởng chơi golf: nửa dưới của kết quả có thể được tạo ra bằng cách sắp xếp từng danh sách con trong nửa đầu và đảo ngược thứ tự.
JungHwan Min

2
@Arnauld [[1,2],[3],[4,5],[6]]Giai đoạn thực sự là giải pháp chính xác, vì sắp xếp hợp nhất đang hoạt động đệ quy. Đó là nếu chúng ta bắt đầu [1,2,3,4,5,6]và chia nó thành [1,2,3][4,5,6]sau đó những danh sách đó được xử lý độc lập cho đến khi chúng được hợp nhất trong bước cuối cùng.
Laikoni

2
@ l4m2 Ok, lần thử cuối cùng cho câu trả lời: 1. bạn cần dấu phân cách vì cũng cần hỗ trợ số nguyên> 9. 2. Điều này không hợp lệ vì lý do tương tự như được đưa ra trong nhận xét của tôi ở trên. Nếu chúng ta tách thành [3][2,1], thì chúng nằm trên các nhánh khác nhau, vì vậy chúng ta không thể hợp nhất [3][2]sau đó [2,1]được chia thành [2][1].
Laikoni

1
Trong thực tế câu sau đó trả lời chính xác câu hỏi của tôi. Xin lỗi vì đã bỏ lỡ điều đó. : P
Zgarb

Câu trả lời:


8

Haskell , 137 128 127 125 121 109 106 byte

(-2) + (- 4) = (- 6) byte nhờ nimi ! Thay đổi nó để thu thập tất cả các bước trong danh sách (một lần nữa do nimi) tiết kiệm thêm 12 byte.

3 byte khác do Laikoni , với ràng buộc bảo vệ mẫu và sử dụng thông minh danh sách để mã hóa bảo vệ.

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

Hãy thử trực tuyến!

Việc chia các danh sách thành các phần tử lẻ và các vị trí chẵn là một mã ngắn hơn so với hai nửa liên tiếp, bởi vì sau đó chúng ta không cần phải đo length.

Hoạt động bằng cách "in" các danh sách, sau đó đệ quy với các danh sách phân tách ( x >>= h) nếu thực sự có bất kỳ sự phân tách nào được thực hiện và "in" các danh sách đã sắp xếp; bắt đầu với một danh sách đầu vào; giả sử đầu vào không trống. Và thay vì in thực tế, chỉ cần thu thập chúng trong một danh sách.

Danh sách được tạo bởi g[[6,5..1]], in từng dòng, là:

[[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=printVà ba lần p. Hãy thử trực tuyến!
nimi

@nimi tuyệt vời, một lần nữa, và cảm ơn rất nhiều! bây giờ nó thực sự trông golfed . :)
Will Ness

Thay vì in trong chức năng, gbạn có thể thu thập tất cả các bước trong danh sách và trả lại. Hãy thử trực tuyến!
nimi

3
Tôi không nghĩ rằng chúng ta có một định nghĩa đúng đắn về "trực quan hóa". Tổng quát hơn các thách thức yêu cầu "xuất" các danh sách và theo mặc định của chúng tôi, điều này có thể được thực hiện thông qua giá trị trả về của hàm . Các câu trả lời khác, ví dụ 1 , 2 cũng làm theo cách này. - Tôi không nghĩ đề xuất của tôi khác nhiều, nó chỉ thu thập các danh sách trung gian thay vì in chúng. Vui lòng chỉnh sửa nó.
nimi

3
g x|y<-x>>=h=x:[z|x/=y,z<-g y++[sort<$>x]]tiết kiệm thêm một số byte.
Laikoni

7

Ngôn ngữ Wolfram (Mathicala) , 146 127 111 102 byte

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

Hãy thử trực tuyến!

Trả về một Listcó chứa các bước.

Giải trình

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

Trong một đầu vào, chia tất cả các Lists chứa 2 hoặc nhiều số nguyên thành một nửa. Danh sách con đầu tiên có các phần tử được lập chỉ mục lẻ (1 chỉ mục) và phần tử thứ hai có các phần tử được lập chỉ mục chẵn.

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

Lặp lại điều đó cho đến khi không có gì thay đổi (tức là tất cả các danh sách con có độ dài-1). Giữ tất cả các kết quả trung gian. Lưu trữ này ( Listtất cả các bước) trong u.

Reverse@Most@u

Thả phần tử cuối cùng uvà đảo ngược nó.

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

Từ kết quả trên, sắp xếp tất cả các lần xuất hiện của một danh sách các số nguyên.


6

Sạch , 228 206 168 157 140 121 104 byte

Xây dựng danh sách các giai đoạn từ đầu đến cuối, sử dụng thực tế là nphần tử -th từ cuối là phiên bản được sắp xếp của nphần tử -th từ đầu.

Ý tưởng từ bình luận của 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]]

Hãy thử trực tuyến!


4

Than , 145 133 123 122 byte

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

Hãy thử trực tuyến! Liên kết là phiên bản dài dòng của mã. Vẫn phải làm việc xung quanh lỗi than ... Chỉnh sửa: Đã lưu 5 byte bằng cách sử dụng gấp đôi Mapnhư một sự hiểu biết mảng của một người nghèo. Đã lưu 4 byte bằng cách sử dụng Popđể nhân đôi số lần lặp trên một mảng. Đã lưu 3 byte bằng cách sử dụng nối thay vì Push. Đã lưu 10 byte bằng cách đưa ra một whileđiều kiện golfer cũng tránh được lỗi Char than. Đã lưu 1 byte bằng cách phát hiện ra rằng Than thực sự có toán tử lọc. Giải trình:

≔⟦⪪S ⟧θ

Tách đầu vào trên các khoảng trắng, sau đó bọc kết quả thành một mảng bên ngoài, lưu nó vào q.

W⊖L§θ⁰«

Lặp lại trong khi phần tử đầu tiên qcó nhiều hơn một phần tử. (Phần tử đầu tiên qluôn có nhiều phần tử nhất do cách các danh sách được chia thành hai.)

⟦⪫Eθ⪫κ ¦|⟧

In các yếu tố của qnối với không gian và đường thẳng đứng. (Mảng khiến kết quả in trên dòng riêng của nó. Có nhiều cách khác để đạt được điều này cho cùng một số byte.)

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

Tạo một danh sách bằng cách sao chép từng phần tử của q, sau đó ánh xạ qua danh sách đó và lấy một nửa của mỗi danh sách (sử dụng phương pháp tiếp cận phần tử thay thế), lưu lại kết quả q.

⟦⪫ΦEθ⪫ι ι|⟧

In các yếu tố của qnối với không gian và đường thẳng đứng. Trên thực tế, tại thời điểm này, các phần tử của qdanh sách phần tử trống hoặc phần tử đơn, do đó, việc nối chúng chỉ chuyển đổi chúng thành chuỗi rỗng hoặc phần tử của chúng. Các chuỗi trống sẽ thêm các dòng theo dõi không cần thiết để chúng được lọc ra. Một người vận hành flatten sẽ là golfier mặc dù (một cái gì đó như ⟦⪫Σθ|⟧).

W⊖Lθ«

Lặp lại trong khi qcó nhiều hơn một yếu tố. (Đoạn mã sau yêu cầu số phần tử chẵn.)

≔⮌θη≔⟦⟧θ

Sao chép qvào h, nhưng đảo ngược (xem bên dưới) và đặt lại qvào một danh sách trống.

Wη«

Lặp lại cho đến khi htrống rỗng.

≔⊟ηε

Trích xuất phần tử tiếp theo hvào e. ( Poptrích từ cuối, đó là lý do tại sao tôi phải đảo ngược q.)

≔⟦⟧ζ

Khởi tạo zmột danh sách trống.

F⊟η«

Lặp lại các yếu tố của các yếu tố tiếp theo của h.

≔εδ≔⟦⟧ε

Sao chép evào dvà đặt lại evào một danh sách trống.

Fδ

Lặp lại các yếu tố của d.

⊞⎇‹IμIλζεμ

Đẩy chúng đến zhoặc etùy thuộc vào việc chúng nhỏ hơn phần tử hiện tại của phần tử tiếp theo h.

⊞ζλ»

Đẩy các yếu tố hiện tại của phần tử tiếp theo của hđể z.

⊞θ⁺ζε»

Liên kết zvới bất kỳ yếu tố nào còn lại evà đẩy nó đến q. Điều này hoàn thành sự hợp nhất của hai yếu tố h.

⟦⪫Eθ⪫κ ¦|

In các yếu tố của qnối với không gian và đường thẳng đứng.


Treo lên. Có một lỗi khác? : /
ASCII - chỉ

@ Chỉ ASCII Không, đây là while (...Map(...)...)lỗi tôi đã nói với bạn.
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`|`:``)

Lấy đầu vào là một mảng trong một mảng, tức là f([[6,5,4,3,2,1]]). Hoạt động bằng cách tạo các dòng đầu tiên và cuối cùng của đầu ra, sau đó phân tách và tự gọi lại cho đến khi mọi mảng con có độ dài 1. Đây là một minh chứng cơ bản về cách thức hoạt động của nó:

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
Vì vậy, có một điểm mà có ba câu trả lời được gắn trên 145 byte?
Neil

2

Husk , 14 byte

S+ȯ†O↔hUmfL¡ṁ½

Có một mảng chứa một mảng duy nhất. Hãy thử trực tuyến!

Giải trình

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.

Tích hợp ½có một mảng và chia nó ở giữa. Nếu độ dài của nó là số lẻ, phần đầu tiên dài hơn bởi một phần tử. Một mảng singleton [a]dẫn đến [[a],[]]và một mảng trống []cung cấp [[],[]], vì vậy cần phải loại bỏ các mảng trống trước khi áp dụng U.


1

Stax , 116 (3>) 38 29 byte CP437

-9 byte mỗi bình luận bởi @recursive. Bây giờ đầu vào được đưa ra dưới dạng một singleton có phần tử duy nhất là một mảng các số được sắp xếp.

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

Hãy thử trực tuyến!

Phiên bản giải nén với 35 byte:

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

Giải trình

Mã có thể được chia thành hai phần. Phần thứ nhất trực quan hóa sự phân tách và phần thứ hai hình dung sự hợp nhất.

Mã để hiển thị chia tách:

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

Mã để trực quan hóa việc hợp nhất:

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

Phiên bản cũ, thực sự xây dựng cấu trúc danh sách lồng nhau.

{{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+= được sử dụng ba lần trong mã để kiểm tra xem đỉnh của ngăn xếp là vô hướng hay là một mảng.

{{cc0+=!{x!}Mm',*:}}Xxây dựng một khối gọi đệ quy chính nó để tạo ra một mảng các số được lồng đúng cách. (Đầu ra mặc định trong Stax vector hóa một mảng lồng nhau trước khi in).

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

Có hai khối khác thực hiện việc tách và sáp nhập tương ứng. Chúng quá dài dòng và tôi không quan tâm giải thích (có thêm một chút thông tin trong phiên bản lịch sử của bài đăng này nhưng đừng kỳ vọng quá nhiều).


Cải thiện rất tốt đẹp. Tôi hoàn toàn không hiểu nó, nhưng tôi nghĩ cH!có thể được sử dụng thay thế cH%!.
đệ quy

{Nd}Mcũng có thể được thay thế bằng T.
đệ quy

Tôi đã thực hiện một số thích ứng hơn. staxlang.xyz/ từ Câu trả lời Husk lấy đầu vào là một mảng trong một mảng, vì vậy tôi đoán đó là hợp pháp.
đệ quy

Tôi tìm thấy một giải pháp nên ngắn hơn 2 ký tự ascii, nhưng tôi phát hiện ra một lỗi trong chuyển đổi mảng. Cụ thể, nó đột biến các hàng mảng. Tôi sẽ thêm nó vào backlog cho 1.0.4
đệ quy

ĐƯỢC. Tôi mong chờ bản cập nhật.
Weijun Zhou
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.