Quicksort-algoritmimplementering i Java

1. Översikt

I den här handledningen utforskar vi QuickSort-algoritmen i detalj med fokus på dess Java-implementering.

Vi diskuterar dess fördelar och nackdelar och analyserar dess tidskomplexitet.

2. QuickSort-algoritm

Quicksort är en sorteringsalgoritm som utnyttjar principen om delning och seger. Den har en genomsnittlig O (n log n) -komplexitet och det är en av de mest använda sorteringsalgoritmerna, särskilt för stora datamängder.

Det är viktigt att komma ihåg att Quicksort inte är en stabil algoritm. En stabil sorteringsalgoritm är en algoritm där elementen med samma värden visas i samma ordning i den sorterade utdata som de visas i inmatningslistan.

Inmatningslistan är uppdelad i två underlistor med ett element som kallas pivot; en underlista med element som är mindre än pivoten och en annan med element som är större än pivoten. Denna process upprepas för varje underlista.

Slutligen slås alla sorterade underlistor samman för att bilda den slutliga utdata.

2.1. Steg för algoritm

  1. Vi väljer ett element från listan, kallat pivot. Vi använder den för att dela upp listan i två underlistor.
  2. Vi ordnar om alla element runt pivoten - de med mindre värde placeras framför den och alla element större än pivoten efter den. Efter detta steg är svängningen i sin slutliga position. Detta är det viktiga partitionssteget.
  3. Vi tillämpar stegen ovan rekursivt på båda underlistorna till vänster och höger om pivoten.

Som vi kan se är quicksort naturligtvis en rekursiv algoritm, som varje delnings- och erövringsmetod.

Låt oss ta ett enkelt exempel för att bättre förstå denna algoritm.

Arr[] = {5, 9, 4, 6, 5, 3}
  1. Låt oss anta att vi väljer 5 som led för enkelhet
  2. Vi sätter först alla element mindre än 5 i matrisens första position: {3, 4, 5, 6, 5, 9}
  3. Vi upprepar det sedan för den vänstra undergruppen {3,4} och tar 3 som led
  4. Det finns inga element mindre än 3
  5. Vi tillämpar quicksort på underarrayen till höger om pivoten, dvs. {4}
  6. Denna undergrupp består av endast ett sorterat element
  7. Vi fortsätter med den högra delen av den ursprungliga matrisen, {6, 5, 9} tills vi får den slutliga ordnade matrisen

2.2. Välja Optimal Pivot

Den avgörande punkten i QuickSort är att välja den bästa pivoten. Mittelementet är naturligtvis det bästa, eftersom det skulle dela upp listan i två lika underlistor.

Men att hitta mittelementet från en oordnad lista är svårt och tidskrävande, det är därför vi tar det första elementet, det sista elementet, medianen eller något annat slumpmässigt element som led.

3. Implementering i Java

Den första metoden är quickSort () som tar som parametrar den matris som ska sorteras, det första och det sista indexet. Först kontrollerar vi indexen och fortsätter bara om det fortfarande finns element att sortera.

Vi får indexet för den sorterade pivoten och använder det för att rekursivt anropa partition () -metoden med samma parametrar som quickSort () -metoden, men med olika index:

public void quickSort(int arr[], int begin, int end) { if (begin < end) { int partitionIndex = partition(arr, begin, end); quickSort(arr, begin, partitionIndex-1); quickSort(arr, partitionIndex+1, end); } }

Låt oss fortsätta med partition () -metoden. För enkelhetens skull tar denna funktion det sista elementet som led. Kontrollera sedan varje element och byt det före pivoten om dess värde är mindre.

Vid slutet av partitioneringen är alla element mindre än ledet till vänster om det och alla element större än ledet är till höger om det. Pivoten är i sin slutliga sorterade position och funktionen returnerar denna position:

private int partition(int arr[], int begin, int end) { int pivot = arr[end]; int i = (begin-1); for (int j = begin; j < end; j++) { if (arr[j] <= pivot) { i++; int swapTemp = arr[i]; arr[i] = arr[j]; arr[j] = swapTemp; } } int swapTemp = arr[i+1]; arr[i+1] = arr[end]; arr[end] = swapTemp; return i+1; }

4. Algoritmeanalys

4.1. Tidskomplexitet

I bästa fall delar algoritmen listan i två lika stora underlistor. Så, den första iterationen av den fullständiga n- storlek-listan behöver O (n) . Att sortera de återstående två underlistorna med n / 2- element tar 2 * O (n / 2) vardera. Som ett resultat har QuickSort-algoritmen komplexiteten O (n log n) .

I värsta fall väljer algoritmen bara ett element i varje iteration, så O (n) + O (n-1) + ... + O (1) , vilket är lika med O (n2) .

I genomsnitt har QuickSort O (n log n) -komplexitet, vilket gör den lämplig för stora datamängder.

4.2. QuickSort vs MergeSort

Låt oss diskutera i vilka fall vi ska välja QuickSort över MergeSort.

Även om både Quicksort och Mergesort har en genomsnittlig tidskomplexitet av O (n log n) är Quicksort den föredragna algoritmen, eftersom den har en O (log (n)) rymdkomplexitet. Mergesort, å andra sidan, kräver O (n) extra lagring, vilket gör det ganska dyrt för matriser.

Quicksort kräver åtkomst till olika index för sin verksamhet, men denna åtkomst är inte direkt möjlig i länkade listor, eftersom det inte finns några kontinuerliga block; därför för att komma åt ett element måste vi iterera genom varje nod från början av den länkade listan. Dessutom implementeras Mergesort utan extra utrymme för LinkedLists.

I ett sådant fall föredras generellt allmänna höjningar för Quicksort och Mergesort.

5. Sammanfattning

Quicksort är en elegant sorteringsalgoritm som är mycket användbar i de flesta fall.

Det är vanligtvis en "på plats" -algoritm, med den genomsnittliga tidskomplexiteten för O (n log n).

En annan intressant punkt att nämna är att Java: s Arrays.sort () -metod använder Quicksort för att sortera arrays av primitiva. Implementeringen använder två ledpunkter och fungerar mycket bättre än vår enkla lösning, det är därför för produktionskod som oftast är bättre att använda biblioteksmetoder.

Som alltid kan koden för implementering av denna algoritm hittas på vårt GitHub-arkiv.