Quicksort algoritmus implementáció Java-ban
1. Áttekintés
Ebben az oktatóanyagban részletesen feltárjuk a QuickSort algoritmust, különös tekintettel a Java megvalósítására.
Megbeszéljük előnyeit és hátrányait is, majd elemezzük időbeli összetettségét.
2. QuickSort algoritmus
A Quicksort egy rendezési algoritmus, amely a divide-and-conquer elvét használja ki. Átlaga van O (n log n) összetettség, és ez az egyik leggyakrabban használt rendezési algoritmus, különösen a nagy adatmennyiségeknél.
Fontos megjegyezni, hogy a Quicksort nem stabil algoritmus. A stabil rendezési algoritmus olyan algoritmus, ahol az azonos értékű elemek ugyanabban a sorrendben jelennek meg a rendezett kimenetben, mint a bemeneti listában.
A bemeneti listát a pivot nevű elem két allistára osztja; az egyik allista az elemeinél kevesebb, mint a pivot, a másik pedig a pivotnál nagyobb elemekkel. Ez a folyamat minden allistán megismétlődik.
Végül az összes rendezett allista összeolvad a végső kimenet képződéséért.
2.1. Algoritmuslépések
- Kiválasztunk egy elemet a listából, amelyet pivotnak hívunk. A listát két allistára osztjuk fel.
- Átrendezzük az összes elemet a forgócsap körül - a kisebb értékűek kerülnek elé, és az összes elem nagyobb, mint az elfordulás az utána. Ezt a lépést követően a csukló végső helyzetben van. Ez a partíció fontos lépése.
- A fenti lépéseket rekurzívan alkalmazzuk a forgás bal és jobb oldalán található mindkét allistára.
Ahogy látjuk, A quicksort természetesen rekurzív algoritmus, mint minden megosztani és meghódítani megközelítés.
Vegyünk egy egyszerű példát az algoritmus jobb megértése érdekében.
Arr [] = {5, 9, 4, 6, 5, 3}
- Tegyük fel, hogy az egyszerűség kedvéért az 5-öt választjuk
- Először az 5-nél kevesebb elemet helyezzük el a tömb első pozíciójában: {3, 4, 5, 6, 5, 9}
- Ezután megismételjük a bal oldali {3,4} altömbnél, 3-at választva elfordulónak
- Nincsenek 3-nál kisebb elemek
- A forgatás jobb oldalán lévő altömbön quicksort alkalmazunk, azaz {4}
- Ez az altömb csak egy rendezett elemből áll
- Az eredeti tömb jobb részével, {6, 5, 9} folytatjuk, amíg meg nem kapjuk a végső rendezett tömböt
2.2. Az Optimal Pivot kiválasztása
A QuickSort döntő eleme a legjobb pivot kiválasztása. A középső elem természetesen a legjobb, mivel két egyenlő részlistára osztaná a listát.
De a középső elem megkeresése egy rendezetlen listáról nehéz és időigényes, ezért vesszük pivotként az első elemet, az utolsó elemet, a mediánt vagy bármely más véletlenszerű elemet.
3. Megvalósítás Java-ban
Az első módszer az quickSort () amely paraméterként veszi a rendezendő tömböt, az első és az utolsó indexet. Először ellenőrizzük az indexeket, és csak akkor folytatjuk, ha vannak még elemek, amelyeket rendezni kell.
Megkapjuk a rendezett pivot indexét, és rekurzív híváshoz használjuk partíció () módszer ugyanazokkal a paraméterekkel, mint a quickSort () módszerrel, de különböző indexekkel:
public void quickSort (int arr [], int begin, int end) {if (start <end) {int partitionIndex = partíció (arr, begin, end); quickSort (arr, begin, partitionIndex-1); quickSort (arr, partitionIndex + 1, end); }}
Folytassuk a partíció () módszer. Az egyszerűség kedvéért ez a függvény az utolsó elemet veszi el pivotként. Ezután ellenőrizze az egyes elemeket, és kicserélje azokat az elfordulás előtt, ha az értéke kisebb.
A particionálás végére a csuklónál kevesebb minden elem a bal oldalán van, és a csuklónál nagyobb elem minden tőle jobbra van. Az elfordulás a végső rendezett helyzetben van, és a függvény ezt a pozíciót adja vissza:
privát int partíció (int arr [], int begin, int end) {int pivot = arr [end]; int i = (kezdet-1); mert (int j = kezdődik; j <vége; 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 [vég]; arr [end] = swapTemp; visszatér i + 1; }
4. Algoritmuselemzés
4.1. Idő komplexitás
A legjobb esetben az algoritmus két azonos méretű allistára osztja a listát. Tehát, a teljes első iterációja nméretű listára van szükség Tovább). A fennmaradó két allista rendezése a n / 2 elemek vesznek 2 * O (n / 2) minden egyes. Ennek eredményeként a QuickSort algoritmus összetettsége O (n log n).
A legrosszabb esetben az algoritmus minden iterációban csak egy elemet választ ki, tehát O (n) + O (n-1) +… + O (1), ami egyenlő a következővel: O (n2).
A QuickSort átlagosan rendelkezik O (n log n) bonyolultsággal, alkalmassá téve nagy adatmennyiségekre.
4.2. QuickSort vs MergeSort
Beszéljük meg, mely esetekben válasszuk a QuickSort-ot a MergeSort helyett.
Bár a Quicksort és a Mergesort átlagos időbeli összetettsége: O (n log n), A Quicksort a preferált algoritmus, mivel rendelkezik egy O (log (n)) tér bonyolultsága. A Mergesort viszont megköveteli Tovább) extra tárhely, ami meglehetősen drága a tömbök számára.
A Quicksortnak különböző indexekhez kell hozzáférnie a műveleteihez, de ez a hozzáférés közvetlenül nem lehetséges a kapcsolt listákban, mivel nincsenek folyamatos blokkok; ezért egy elem eléréséhez meg kell ismételnünk az egyes csomópontokat a csatolt lista elejétől. Ezenkívül a Mergesort külön hely nélkül valósul meg LinkedLists.
Ebben az esetben általában a Quicksort és a Mergesort általános költségnövekedését részesítik előnyben.
5. Következtetés
A Quicksort egy elegáns rendezési algoritmus, amely a legtöbb esetben nagyon hasznos.
Általában „helyben” lévő algoritmus, amelynek átlagos időbeli összetettsége O (n log n).
Egy másik érdekes megemlítendő tény, hogy a Java-ok Tömbök. Rendezés () módszer a primitívek tömbjeinek rendezéséhez használja a Quicksort alkalmazást. A megvalósítás két forgatókönyvet használ, és sokkal jobban teljesít, mint az egyszerű megoldás, ezért a termelési kód esetében általában jobb a könyvtár módszerét használni.
Mint mindig, az algoritmus megvalósításának kódja megtalálható a GitHub adattárunkban.