Flet sortering | |
---|---|
| |
Forfatter | John von Neumann |
formål | Sorteringsalgoritme |
Datastruktur | liste , matrix |
værste tid | |
Bedste tid | |
Gennemsnitlig tid | |
Hukommelsesomkostninger | for liste, for array |
Mediefiler på Wikimedia Commons |
Merge sort er en sorteringsalgoritme , der arrangerer lister (eller andre datastrukturer, hvis elementer kun kan tilgås sekventielt , for eksempel streams ) i en bestemt rækkefølge. Denne sortering er et godt eksempel på brug af opdel og hersk princippet . Først er opgaven opdelt i flere mindre delopgaver. Disse opgaver løses så med et rekursivt kald eller direkte, hvis deres størrelse er lille nok. Til sidst kombineres deres løsninger , og der opnås en løsning på det oprindelige problem.
For at løse sorteringsproblemet ser disse tre trin således ud:
1.1. - 2.1. Rekursiv opdeling af opgaven i mindre opstår, indtil størrelsen af arrayet når en (enhver array af længde 1 kan betragtes som ordnet).
3.1. Kombinere to ordnede arrays til én.
Den grundlæggende idé med at flette to sorterede arrays kan forklares med følgende eksempel. Antag, at vi allerede har to subarrays sorteret i stigende rækkefølge. Derefter:
3.2. Sammenlægning af to underarrays til et tredje resulterende array.
Ved hvert trin tager vi det mindste af de to første elementer i underarrayerne og skriver det til det resulterende array. Tællerne for antallet af elementer i det resulterende array og subarrayet, hvorfra elementet blev taget, øges med 1.
3.3. "Vedhæftning" af resten.
Når et af underarrays er forbi, tilføjer vi alle de resterende elementer i det andet underarray til det resulterende array.
Sorteringseksempel i C
/** * Sorterer array ved hjælp af rekursiv flet sortering * op - pegepind til array, der skal sorteres * ned - peger til array med mindst samme størrelse som 'op', bruges som buffer * venstre-venstre kant af array , pass 0 til sorter arrayet fra begyndelsen * højre - højre kant af arrayet, passér længden af arrayet - 1 for at sortere arrayet til det sidste element * returnerer: en pointer til det sorterede array. På grund af hvordan denne implementering fungerer * kan den sorterede version af arrayet ende enten i 'op' eller 'ned' **/ int * merge_sort ( int * op , int * ned , usigneret int venstre , usigneret int højre ) { hvis ( venstre == højre ) { ned [ venstre ] = op [ venstre ]; vende tilbage ned ; } unsigned int middle = left + ( højre - venstre ) / 2 ; // del og sorter int * l_buff = merge_sort ( op , ned , venstre , midt ); int * r_buff = merge_sort ( op , ned , midt + 1 , højre ); // flette to sorterede halvdele int * target = l_buff == op ? ned : op ; usigneret int l_cur = venstre , r_cur = midt + 1 ; for ( usigneret int i = venstre ; i <= højre ; i ++ ) { if ( l_cur <= midt && r_cur <= højre ) { if ( l_buff [ l_cur ] < r_buff [ r_cur ]) { mål [ i ] = l_buff [ l_cur ]; l_cur ++ ; } andet { target [ i ] = r_buff [ r_cur ]; r_cur ++ ; } } else if ( l_cur <= middle ) { mål [ i ] = l_buff [ l_cur ]; l_cur ++ ; } andet { target [ i ] = r_buff [ r_cur ]; r_cur ++ ; } } returmål ; _ }Implementering i C++11 :
#include <algoritme> #include <cstddef> #inkluder <iterator> #inkluder <hukommelse> skabelon < typenameT > _ void merge_sort ( T array [], std :: size_t size ) noexcept { hvis ( størrelse > 1 ) { std :: size_t const left_size = størrelse / 2 ; std :: størrelse_t const højre_størrelse = størrelse - venstre_størrelse ; merge_sort ( & array [ 0 ], left_size ); merge_sort ( & array [ venstre_størrelse ], højre_størrelse ); std :: størrelse_t lidx = 0 , ridx = venstre_størrelse , idx = 0 ; std :: unik_ptr < T [] > tmp_array ( ny T [ størrelse ]); while ( lidx < left_size || ridx < size ) { if ( array [ lidx ] < array [ ridx ]) { tmp_array [ idx ++ ] = std :: move ( array [ lidx ]); lidx ++ ; } andet { tmp_array [ idx ++ ] = std :: move ( array [ ridx ]); ridx ++ ; } if ( lidx == venstre_størrelse ) { std :: copy ( std :: make_move_iterator ( & array [ ridx ]), std :: make_move_iterator ( & array [ størrelse ]), & tmp_array [ idx ]); bryde ; } if ( ridx == størrelse ) { std :: copy ( std :: make_move_iterator ( & array [ lidx ]), std :: make_move_iterator ( & array [ left_size ]), & tmp_array [ idx ]); bryde ; } } std :: copy ( std :: make_move_iterator ( tmp_array ), std :: make_move_iterator ( & tmp_array [ størrelse ]), array ); } }Implementering i C++14 med OpenMP parallelisering
#include <algoritme> #inkluder <iterator> #include <omp.h> #inkluder <hukommelse> skabelon < typenameIterator > _ void mergesort ( Iterator fra , Iterator til ) { #pragma omp parallel { #pragma omp single nowait static_assert ( ! std :: er_samme < typenavn std :: iterator_træk < Iterator >:: værditype , ugyldig >:: værdi ); auto n = std :: distance ( fra , til ); hvis ( 1 < n ) { #pragma omp opgave førstprivat (fra, til, n) { Iterator l_fra = fra ; Iterator l_to = l_fra ; std :: advance ( l_to , n / 2 ); mergesort ( l_fra , l_til ); } #pragma omp opgave førstprivat (fra, til, n) { Iterator r_from = fra ; std :: advance ( r_fra , n / 2 ); Iterator r_to = r_fra ; std :: advance ( r_to , n - ( n / 2 )); mergesort ( r_fra , r_til ); } #pragma omp taskwait auto tmp_array = std :: make_unique < typename Iterator :: værditype [] > ( n ); Iterator l_iter = fra ; Iterator l_end = l_iter ; std :: advance ( l_end , n / 2 ); Iterator r_iter = l_end ; Iterator & r_end = til ; auto tmp_iter = tmp_array . få (); mens ( l_iter != l_end || r_iter != r_end ) { if ( * l_iter < * r_iter ) { * tmp_iter = std :: move ( * l_iter ); ++ l_iter ; ++ tmp_iter ; } andet { * tmp_iter = std :: move ( * r_iter ); ++ r_iter ; ++ tmp_iter ; } if ( l_iter == l_end ) { std :: kopi ( std :: make_move_iterator ( r_iter ), std :: make_move_iterator ( r_end ), tmp_iter ); bryde ; } if ( r_iter == r_end ) { std :: kopi ( std :: make_move_iterator ( l_iter ), std :: make_move_iterator ( l_end ), tmp_iter ); bryde ; } } std :: kopi ( std :: make_move_iterator ( tmp_array.get ( ) ), std :: make_move_iterator ( & tmp_array [ n ]), fra ); } } }Iterativ implementering i C++ :
skabelon < typenameT > _ void MergeSort ( T a [], size_t l ) { size_t BlockSizeIterator ; size_t BlockIterator ; size_t LeftBlockIterator ; size_t RightBlockIterator ; size_t MergeIterator ; size_t LeftBorder ; size_t MidBorder ; size_t RightBorder ; for ( BlockSizeIterator = 1 ; BlockSizeIterator < l ; BlockSizeIterator *= 2 ) { for ( BlockIterator = 0 ; BlockIterator < l - BlockSizeIterator ; BlockIterator += 2 * BlockSizeIterator ) { //Vi fusionerer med sortering af et par blokke startende fra BlockIterator-elementet //den venstre med størrelsen BlockSizeIterator, den højre med størrelsen BlockSizeIterator eller mindre LeftBlockIterator = 0 ; RightBlockIterator = 0 ; LeftBorder = BlockIterator ; MidBorder = BlockIterator + BlockSizeIterator ; RightBorder = BlockIterator + 2 * BlockSizeIterator ; RightBorder = ( RightBorder < l ) ? RightBorder : l ; int * SortedBlock = new int [ RightBorder - LeftBorder ]; //Mens begge arrays har elementer, vælg den mindste og læg den i den sorterede blok, mens ( LeftBorder + LeftBlockIterator < MidBorder && MidBorder + RightBlockIterator < RightBorder ) { if ( a [ LeftBorder + LeftBlockIterator ] < a [ MidBorder + RightBlockIterator ]) { SortedBlock [ LeftBlockIterator + RightBlockIterator ] = en [ LeftBorder + LeftBlockIterator ]; LeftBlockIterator += 1 ; } andet { SortedBlock [ LeftBlockIterator + RightBlockIterator ] = en [ MidBorder + RightBlockIterator ]; RightBlockIterator += 1 ; } } //Derefter henter vi de resterende elementer fra venstre eller højre blok, mens ( LeftBorder + LeftBlockIterator < MidBorder ) { SortedBlock [ LeftBlockIterator + RightBlockIterator ] = en [ LeftBorder + LeftBlockIterator ]; LeftBlockIterator += 1 ; } while ( MidBorder + RightBlockIterator < RightBorder ) { SortedBlock [ LeftBlockIterator + RightBlockIterator ] = en [ MidBorder + RightBlockIterator ]; RightBlockIterator += 1 ; } for ( MergeIterator = 0 ; MergeIterator < LeftBlockIterator + RightBlockIterator ; MergeIterator ++ ) { a [ LeftBorder + MergeIterator ] = SortedBlock [ MergeIterator ]; } slet SortedBlock ; } } }
Implementering i Python :
Pseudokode for flettealgoritmen uden "vedhæftning" af resten i et C++- lignende sprog:
Algoritmen blev opfundet af John von Neumann i 1945 [1] .
Ovenstående algoritme i et C++-lignende sprog bruger en kontrol for lighed af to sammenlignede elementer af subarrays med en separat behandlingsblok i tilfælde af lighed. En separat lighedskontrol fordobler antallet af sammenligninger, hvilket komplicerer programkoden. I stedet for en separat lighedskontrol og en separat lighedsbehandlingsblok, kan du bruge to kontroller if(L <= R) og if(L >= R) , hvilket næsten halverer programmets kode.
Pseudo-kode for en forbedret flettealgoritme uden at "vedhæfte" resten i et C++- lignende sprog:
L = *Inl; R = *In2; if( L <= R ) { *Ud++ = L; In1++; } if(L >= R) { *Ud++ = R; In2++; }Antallet af checks kan halveres ved at fjerne if(L >= R) markeringen . I dette tilfælde, hvis L og R er ens, vil L blive skrevet til Ud i den første iteration, og R - i den anden. Denne mulighed vil fungere effektivt, hvis det originale array ikke har duplikerede elementer, der dominerer over resten af elementerne.
Pseudokode for en forkortet flettealgoritme uden at "vedhæfte" resten i et C++- lignende sprog:
L = *Inl; R = *In2; if( L <= R ) { *Ud++ = L; In1++; } andet { *Ud++ = R; In2++; }De to overførselsoperationer til variablerne L og R forenkler nogle af programindtastningerne, hvilket kan være nyttigt til undervisningsformål, men i rigtige programmer kan de fjernes, hvilket vil reducere programkoden. Du kan også bruge den ternære operator , som vil reducere programkoden yderligere.
Pseudokode for en endnu mere avanceret flettealgoritme uden at "vedhæfte" resten i et C++- lignende sprog:
Algoritmens køretid er O(n * log n) i fravær af nedbrydning på dårlige tilfælde, hvilket er et ømt punkt for quicksort (også en algoritme af orden O(n * log n), men kun for det gennemsnitlige tilfælde ). Hukommelsesforbruget er højere end for hurtig sortering, med et meget mere gunstigt hukommelsestildelingsmønster - det er muligt at allokere én region af hukommelsen helt fra begyndelsen og ikke at allokere senere.
En populær implementering kræver en engangsallokeret midlertidig hukommelsesbuffer svarende til det array, der sorteres, og har ingen rekursioner. Implementeringstrin:
Denne implementering understøtter også placering af arrayet, der skal sorteres, og den midlertidige buffer i diskfiler, hvilket gør den velegnet til at sortere enorme mængder data. Implementeringen af ORDER BY i MySQL i mangel af et passende indeks fungerer præcis sådan (kilde: filesort.cc i MySQL-kildekoden).
Et eksempel på implementering af en simpel to-vejs flettealgoritme i pseudokode:
funktion mergesort(m) var liste venstre, højre, resultat hvis længde(m) ≤ 1 returner m andet midt = længde(m) / 2 for hvert x i m op til midten tilføje x til venstre for hvert x i m efter midten tilføje x til højre venstre = mergesort(venstre) højre = mergesort(højre) resultat = flet (venstre, højre) returnere resultat ende hvisDer er flere varianter af merge()-funktionen, den enkleste kan se sådan ud:
funktion flet (venstre, højre) var listeresultat , mens længde (venstre) > 0 og længde (højre) > 0, hvis først (venstre) ≤ første (højre) føje først (venstre) til resultatet venstre = hvile (venstre) andet føje først (højre) til resultatet højre = hvile (højre) ende hvis mens længde (venstre) > 0 føje først (venstre) til resultatet venstre = hvile (venstre) mens længde (højre) > 0 føje først (højre) til resultatet højre = hvile (højre) returnere resultatFordele:
Fejl:
Sorteringsalgoritmer | |
---|---|
Teori | Kompleksitet O notation Ordreforhold Sorter typer bæredygtige Indre Ekstern |
Udveksling | |
Valg | |
indsætter | |
fusion | |
Ingen sammenligninger | |
hybrid | |
Andet | |
upraktisk |