Sortowanie dla zaawansowanychGdy trzeba posortować jakąś kolekcję w sposób - nazwijmy to - standardowy, to zwykle nie ma z tym problemu. Chyba każdy język posiada coś na takie okazje: mamy funkcję qsort, std::sort (i std::list::sort), System.Array.Sort, java.util.Arrays.sort, i tak dalej. Zwykle wystarczy podać im tablicę lub kolekcję i już mamy ją posortowaną.
Istnieją jednak przynajmniej dwie sytuacje, gdzie taka standardowa procedura nie jest wystarczająca. Jest tak na przykład wtedy, gdy:
<, >=, itp.), które nie muszą być zdefiniowane (np. jeśli sortujemy obiekty własnego typu złożonego) lub mogą być określone inaczej niż byśmy w danym przypadku sortowania chcieli.W powyższych przypadkach rozwiązanie sprowadza się zwykle do zdefiniowania własnego kryterium porównawczego obiektów, które sortujemy. Spotyka się tu najczęściej dwie formy takiego kryterium:
<, czyli zwracać true, jeśli pierwszy obiekt jest "mniejszy" niż drugi. Jak nietrudno zauważyć, taka informacja jest wystarczająca do jednoznacznego uporządkowania dwóch obiektów. W szczególności a == b zachodzi wtedy i tylko wtedy, gdy !(a < b) && !(b < a), zatem taki predykat może też służyć do sprawdzania, czy dwa obiekty są sobie równe.std::less, który działa identycznie jak operator <. std::greater pozwala natomiast na odwrócenie porządku sortowania.| Wynik | Relacja |
| < 0 | x < y |
| == 0 | x == y |
| > 0 | x > y |
W C, C# i w Javie korzysta się z kolei z funkcji porządkujących, które przyjmują dwa obiekty i zwracają w wyniku liczbę całkowitą. Liczba ta określa jednoznacznie uporządkowanie tych dwóch obiektów: czy jeden jest mniejszy niż drugi, równy mu czy większy od niego - tak, jak to pokazano w tabelce obok.
Interesującą cechą takiego sposobu porządkowania jest to, że tak naprawdę używanie jakichkolwiek porównań (<=, > itp.) często nie jest w nim potrzebne. Zazwyczaj bowiem ową funkcję porównującą można zaimplementować następująco:
gdzie val(x) oznacza tutaj pewną wartość liczbową przyporządkowaną obiektowi x, określającą jego miejsce w danym porządku sortowania. Wartość ta powinna być tak dobrana, aby posortowanie wszystkich takich liczb rosnąco dało nam w wyniku pożądaną kolejność uporządkowania naszych obiektów. Jeśli więc, przykładowo, chcemy posortować ciągi znaków w C# tak, by najdłuższe znalazły się na początku (czyli malejąco względem długości), to val(x) oznaczać tu będzie -x.Length().
Uff, jak widać sortowanie czasami nie jest wcale taką prostą sprawą :) Cieszmy się jednak, że nawet w bardziej skomplikowanych sytuacjach do napisania pozostają nam jedynie proste predykaty porównujące, nie zaś... całe algorytmy ;]
Warto wspomnieć, że w przypadku C# czasami całkiem ładnie wygląda podanie funkcji sortującej jako lambda wyrażenia.
std.algorithm.sort!("a > b")(JakiśZakres);
Ot - D.
Obecnie skłaniam się ku opinii, że funkcja/metoda porównująca która zwraca int odpowiednio 0 to najlepsze rozwiązanie i wszelkie operatory == <= itd. warto zaimplementować właśnie z jej użyciem.
Reg: Tak, ale IMHO jest tak tylko wtedy, jeśli możesz ją zaimplementować w postaci takiej, jak napisałem wyżej, czyli odejmowania val(x) - val(y). A to oznacza, że musisz jakoś określić liczbową wartość każdego obiektu. Najczęściej da się każdemu obiektowi przypisać jakąś sensowną liczbę, ale jeśli nie, to napisanie funkcji compare() musiałoby się oprzeć o porównania - a to już ładne nie jest ;]
Patrząc na liczbę komentarzy to mało interesująca notka :P
Prawdę mówiąc, jako, że właśnie się przymierzałem do zdobycia umiejętności zaimplementowania jakiegoś algorytmu sortowania (który nie jest sortowaniem bombelkowym :P) spodziewałem się zobaczyć jakiś ciekawy algorytm sortowania, tym bardziej ostatnie zdanie mnie dobiło :D Ale dobra.. Motyka w dłoń :)
Newline tags are added automatically.
For code, use [code][/code]. You can also insert LaTeX formulae inside [tex][/tex].
HTML tags allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>