insertion sort (megszámlálható és megszámlálhatatlan, tsz. insertion sorts)
Az insertion sort (beszúró rendezés) egy egyszerű, de hatékony rendezési algoritmus kis és félig rendezett tömbök esetén. Működése során fokozatosan bővíti a már rendezett részhalmazt azzal, hogy a vizsgált elemet a megfelelő helyre szúrja be. Bár időkomplexitása négyzetes (O(n²)), a gyakorlati futása kis n értékeknél versenyképes lehet, ráadásul könnyen megérthető és megvalósítható C++ nyelven.
a
tömb, melynek elemei a
–tól a
-ig terjednek. A nulladik elem (a
) önmagában rendezettnek tekinthető.i=1
–től i=n-1
–ig. Minden lépésben a key = a
értéket „felvesszük” és beszúrjuk a már rendezett részhalmazba (a
), úgy hogy végigbalra tolunk minden nagyobb elemet, majd a key
-t a szabaddá vált helyre helyezzük.0..i
indexek közötti szegmens rendezetté válik. A végén – amikor i=n-1
is beillesztésre került – az egész tömb rendezett.
InsertionSort(a, n):
for i = 1 to n-1:
key = a
j = i - 1
while j ≥ 0 and a > key:
a = a
j = j - 1
a = key
#include <iostream>
#include <vector>
// Beszúró rendezés függvénye
void insertionSort(std::vector<int>& a) {
int n = static_cast<int>(a.size());
for (int i = 1; i < n; ++i) {
int key = a;
int j = i - 1;
// Toljuk jobbra a nagyobb elemeket
while (j >= 0 && a > key) {
a = a;
--j;
}
// Beszúrás a megfelelő helyre
a = key;
}
}
int main() {
std::vector<int> arr = {12, 11, 13, 5, 6};
std::cout << "Eredeti tömb: ";
for (int x : arr) std::cout << x << ' ';
std::cout << std::endl;
insertionSort(arr);
std::cout << "Rendezett tömb: ";
for (int x : arr) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
Magyarázat
std::vector<int>& a
referenciaként kapott tömböt módosítjuk helyben.for
-ciklus az aktuális „beszúrandó” elem indexét (i
) futja végig.while
-ciklus a rendezett tartomány elemeit tolja jobbra addig, amíg nem talál kisebb vagy egyenlő értéket a key
-re.key
beillesztése után a tömb elemei az 0..i
tartományban rendezetté válnak.
key
, i
, j
) szükséges.
while
-ciklus sosem fut).
Az egyszerű while
-ciklus helyett a beszúrási pozíciót bináris kereséssel is meghatározhatjuk, így az összehasonlítások száma csökkenthető O(log n)-re, miközben az elemek eltolásának száma továbbra is O(n) marad:
#include <algorithm> // std::upper_bound
void insertionSortBinary(std::vector<int>& a) {
int n = static_cast<int>(a.size());
for (int i = 1; i < n; ++i) {
int key = a;
// Megkeressük a beszúrás helyét bináris kereséssel
int insertPos = std::upper_bound(a.begin(), a.begin() + i, key) - a.begin();
// Előre másoljuk a beszúrandó elemet
for (int j = i; j > insertPos; --j) {
a = a;
}
a = key;
}
}
Tegyük fel, hogy az a =
. A rendezés folyamata:
key=4
): rendezett rész
. 8 > 4 ⇒ tolás →
, majd beszúrás →
.key=5
): rendezett rész
. 8 > 5 ⇒ tolás →
. 4 < 5 ⇒ megállunk, beszúrunk a j+1=1
pozícióba →
.key=3
): rendezett rész
. 8 > 3 →
; 5 > 3 →
; 4 > 3 →
; j = -1 → beszúrás index 0 →
. Végül rendezett tömb:
.
Tulajdonság | Insertion Sort | Quick Sort | Merge Sort | Heap Sort |
---|---|---|---|---|
Idő átlag/legrossz | O(n²) | O(n log n) | O(n log n) | O(n log n) |
Idő legjobb | O(n) | O(n log n) | O(n log n) | O(n log n) |
Helyigény | O(1) | O(log n) | O(n) | O(1) |
Stabilitás | Igen | Nem (gyakran) | Igen | Nem |
Kis tömbre ajánlott | Igen | Nem | Nem | Nem |
Látható, hogy bár nagy adatmennyiségre a *Quick Sort*, *Merge Sort* és *Heap Sort* hatékonyabbak, az insertion sort előnye a stabilitás és a nagyon kis tömbökön, illetve részben rendezett adatokon felülmúlhatatlan egyszerűsége és alacsony állandó tényezője.
Az insertion sort – bár időkomplexitása O(n²) – könnyen megérthető, stabil és helytakarékos rendező algoritmus. C++-ban néhány soros implementációval gyorsan használatba vehető, és kis vagy részben rendezetten levő adatok esetén akár jobb is lehet a bonyolultabb O(n log n) algoritmusoknál. Ha a rendezendő tömb mérete nagy, akkor érdemes gyorsabb algoritmusokat alkalmazni, de az insertion sort továbbra is alapvető eszköze a programozási ismereteknek és a valós alkalmazásoknak egyaránt.