Sor (queue)

  • Informatika
  • Adatszerkezet
  • FIFO elv
  • Körkörös tömb
  • Enqueue Dequeue
  • Időkomplexitás O(1)
  • Láncolt lista implementáció

Podcast

Sor adatszerkezet: A láthatatlan rendszer, ami a világunkat mozgatja0:00 / 11:54
0:001:00 remaining

Mi az a Sor (Queue) adatszerkezet és miért fontos?

A sor egy alapvető lineáris adatszerkezet a számítástechnikában, amely a valós életben is gyakran előforduló sorban állás elvét modellezi. Gondoljunk például egy bolti pénztár előtti sorra, ahol az elsőként érkező vásárló az első, akit kiszolgálnak. Az utoljára érkezőnek pedig ki kell várnia a sorát. Ez az elv a „First-In, First-Out” (FIFO) néven ismert, ami magyarul azt jelenti: „elsőként be, elsőként ki”. A sor adatszerkezet pontosan ezen elv alapján működik: az elemeket a sor egyik végén adjuk hozzá, és a sor másik végéről vesszük ki. Ez teszi a sort rendkívül hasznos eszközzé számos programozási feladatban és rendszertervezésben.

A Sor (Queue) alapvető műveletei és működése

Ahhoz, hogy egy adatszerkezetet sornak nevezhessünk, néhány alapvető műveletet kell támogatnia. Ezek a műveletek garantálják a FIFO elv betartását és a hatékony működést.

Elemek hozzáadása a sorhoz: Enqueue művelet

Az Enqueue művelet egy új elemet ad hozzá a sor végéhez (más néven a „rear” vagy „farok” végéhez). Ha a sor tele van (fix méretű implementáció esetén), akkor a művelet sikertelen lehet, vagy hibát jelezhet. Ez biztosítja, hogy az új elemek mindig a sor végére kerüljenek, várva a feldolgozásukra.

Elemek eltávolítása a sorból: Dequeue művelet

A Dequeue művelet eltávolítja és visszaadja a sor elején lévő elemet (más néven a „front” vagy „fej” végéről). Ha a sor üres, akkor a művelet sikertelen lehet, vagy hibát jelezhet. Ezzel az elsőként bekerült elem kerül ki elsőként.

További fontos sor műveletek

  • Első elem lekérdezése (Front/Peek): Ez a művelet visszaadja a sor elején lévő elemet anélkül, hogy eltávolítaná azt. Hasznos lehet az elem megtekintésére a feldolgozás előtt.
  • Üres-e (isEmpty): Ez a művelet logikai értéket ad vissza, jelezve, hogy a sor tartalmaz-e elemeket (true, ha üres, false, ha nem).
  • Tele van-e (isFull): Ez a művelet (fix méretű sorok esetén) logikai értéket ad vissza, jelezve, hogy a sor elérte-e maximális kapacitását.

A Sor (Queue) időkomplexitása: Miért hatékony?

Az alapvető sor műveletek, mint a beillesztés (enqueue) és a kivétel (dequeue), ideális esetben O(1) időkomplexitással rendelkeznek. Ez azt jelenti, hogy a művelet végrehajtásához szükséges idő nem függ a sorban lévő elemek számától. Függetlenül attól, hogy a sorban 10 vagy 10 millió elem van, a művelet végrehajtásához nagyjából ugyanannyi időre van szükség. Ez rendkívül hatékonnyá teszi a sort olyan alkalmazásokban, ahol gyorsan kell elemeket hozzáadni és eltávolítani.

Check what you already know

Test · 20 questions · first 3 free

Question 1 of 30%

A verem adatszerkezet a FIFO (First-In, First-Out) elv alapján működik.

Practise it with flashcards

35 cards · first 3 free, no account

1 / 35

Mi az a sor (adatszerkezet), és milyen alapelven működik?

A sor egy alapvető lineáris adatszerkezet, ami a valós életben is gyakran előforduló sorban állás elvét modellezi. Az "elsőként be, elsőként ki" (Firs

Tap to flip · Swipe to navigate

Sor (Queue) implementációk: Tömb vagy láncolt lista?

A sor adatszerkezetet többféleképpen is meg lehet valósítani, de a leggyakoribbak a láncolt lista alapú és a tömb alapú implementációk. Mindkettőnek megvannak a maga előnyei és hátrányai.

Láncolt lista alapú sor implementáció

Ebben az esetben a sor elemeit egy láncolt lista csomópontjai tárolják. Két mutatót tartunk nyilván: egyet a sor elejére (front) és egyet a sor végére (rear). Az enqueue művelet során egy új csomópontot hozunk létre, és a rear mutatót erre állítjuk, majd az előző rear csomópont következő mutatóját az új csomópontra állítjuk. A dequeue művelet során egyszerűen a front mutató által mutatott csomópontot távolítjuk el, és a front mutatót a következő csomópontra léptetjük. Ez az implementáció dinamikus méretű, azaz a sor mérete a rendelkezésre álló memória erejéig tetszőlegesen növekedhet.

Tömb alapú sor implementáció és hátrányai

Egy egyszerű tömb használata esetén a front és rear mutatók a tömb indexeire mutatnak. Az enqueue művelet a rear indexénél helyezi el az elemet, majd növeli a rear indexet. A dequeue művelet a front indexénél lévő elemet adja vissza, majd növeli a front indexet. Ennek az egyszerű megvalósításnak azonban van egy jelentős hátránya: minden dequeue művelet után a tömb elején „üres helyek” keletkeznek, és a front és rear mutatók folyamatosan a tömb végéhez közelednek. Végül a tömb megtelik, még akkor is, ha valójában sok üres hely van az elején. Ennek orvoslására az elemeket minden dequeue után el lehetne tolni a tömb elejére, de ez O(n) időkomplexitású lenne, ami rontaná a sor hatékonyságát.

A körkörös tömb (Circular Array) mint hatékony sor megoldás

A tömb alapú sor implementáció problémáit a körkörös tömb (vagy körkörös puffer) oldja meg. Ez a megközelítés egy fix méretű tömböt használ, de a tömb végét és elejét „összeköti”, mintha egy kör lenne. Amikor a rear vagy front mutató eléri a tömb végét, egyszerűen visszaugrik a tömb elejére (0-ás indexre). Ezt a modulo operátor (%) segítségével valósítjuk meg.

Legyen kapacitás a tömb maximális mérete. A front és rear mutatók a tömb indexeit jelölik.

  • Inicializálás: Kezdetben front = -1 és rear = -1, jelezve, hogy a sor üres. Alternatív megoldásként front = 0 és rear = -1, vagy front = 0 és rear = 0 (és egy méret változóval követjük az elemek számát).
  • isEmpty() ellenőrzés: A sor üres, ha front = -1 (vagy ha méret = 0).
  • isFull() ellenőrzés: A sor tele van, ha (rear + 1) % kapacitás == front (feltételezve, hogy egy üres helyet mindig fenntartunk a megkülönböztetéshez), vagy ha méret == kapacitás.

enqueue művelet körkörös tömbben lépésről lépésre

  1. Ellenőrizzük, hogy a sor tele van-e. Ha igen, hibát jelzünk.
  2. Ha a sor üres (front == -1), akkor front = 0.
  3. Növeljük a rear mutatót: rear = (rear + 1) % kapacitás.
  4. Helyezzük el az új elemet a tömb rear indexénél: tömb[rear] = új_elem.
  5. Növeljük a méret változót.

dequeue művelet körkörös tömbben lépésről lépésre

  1. Ellenőrizzük, hogy a sor üres-e. Ha igen, hibát jelzünk.
  2. Tároljuk el a tömb[front] indexen lévő elemet, amit visszaadunk.
  3. Csökkentjük a méret változót.
  4. Ha a sor üressé vált (méret == 0), akkor front = -1 és rear = -1 (vagy front = rear = 0, ha a méret változót használjuk az üres állapot jelzésére).
  5. Ellenkező esetben növeljük a front mutatót: front = (front + 1) % kapacitás.
  6. Visszaadjuk az eltárolt elemet.

Ez az implementáció biztosítja, hogy a front és rear mutatók a tömbön belül maradjanak, és hatékonyan kihasználják a rendelkezésre álló memóriát anélkül, hogy elemeket kellene mozgatni.

Példa a körkörös tömb működésére (5 elemű tömb)

Nézzük meg egy 5 elemű (0-tól 4-ig indexelt) körkörös tömb példáján keresztül, hogyan mozognak a mutatók és változik a sor tartalma. Kezdetben front = -1, rear = -1, méret = 0.

  1. enqueue(10): Sor üres, front = 0. rear = 0. tömb[0] = 10. méret = 1. (Sor: [10, •, •, •, •], front=0, rear=0)
  2. enqueue(20): rear = 1. tömb[1] = 20. méret = 2. (Sor: [10, 20, •, •, •], front=0, rear=1)
  3. enqueue(30): rear = 2. tömb[2] = 30. méret = 3. (Sor: [10, 20, 30, •, •], front=0, rear=2)
  4. dequeue(): Elem: tömb[0] (10). méret = 2. front = 1. Visszaadott érték: 10. (Sor: [•, 20, 30, •, •], front=1, rear=2)
  5. enqueue(40): rear = 3. tömb[3] = 40. méret = 3. (Sor: [•, 20, 30, 40, •], front=1, rear=3)
  6. enqueue(50): rear = 4. tömb[4] = 50. méret = 4. (Sor: [•, 20, 30, 40, 50], front=1, rear=4)
  7. enqueue(60): rear = 0 (körbefordul). tömb[0] = 60. méret = 5. (Sor: [60, 20, 30, 40, 50], front=1, rear=0). Figyeljük meg, hogy a rear visszaugrott a tömb elejére.
  8. isFull(): (rear + 1) % 5 == (0 + 1) % 5 = 1. front = 1. Tehát (rear + 1) % kapacitás == front igaz. A sor tele van.
  9. enqueue(70): Hiba: a sor tele van.
  10. dequeue(): Elem: tömb[1] (20). méret = 4. front = 2. Visszaadott érték: 20. (Sor: [60, •, 30, 40, 50], front=2, rear=0)
  11. dequeue(): Elem: tömb[2] (30). méret = 3. front = 3. Visszaadott érték: 30. (Sor: [60, •, •, 40, 50], front=3, rear=0)
  12. dequeue(): Elem: tömb[3] (40). méret = 2. front = 4. Visszaadott érték: 40. (Sor: [60, •, •, •, 50], front=4, rear=0)
  13. dequeue(): Elem: tömb[4] (50). méret = 1. front = 0 (körbefordul). Visszaadott érték: 50. (Sor: [60, •, •, •, •], front=0, rear=0)
  14. dequeue(): Elem: tömb[0] (60). méret = 0. A sor üressé vált, front = -1, rear = -1. Visszaadott érték: 60. (Sor: [•, •, •, •, •], front=-1, rear=-1)
  15. isEmpty(): Igaz. A sor üres.
  16. dequeue(): Hiba: a sor üres.

Sor (Queue) felhasználási területek a mindennapokban és az informatikában

A sor adatszerkezet rendkívül sokoldalú, és számos területen alkalmazzák a számítástechnikában. Segít rendezni, ütemezni és hatékonyan kezelni az egymás után érkező feladatokat vagy adatokat.

  • Nyomtatósorok (Printer Queues): Amikor több felhasználó küld nyomtatási feladatot egy hálózati nyomtatónak, a feladatok egy sorba kerülnek. A nyomtató a FIFO elv alapján dolgozza fel őket, biztosítva, hogy az elsőként érkező feladat nyomtatódjon ki elsőként.
  • Operációs rendszerek (Operating Systems): A feladatütemezők (schedulers) gyakran használnak sorokat a folyamatok kezelésére. Például egy CPU ütemező sorba állíthatja a futásra váró folyamatokat, és a FIFO elv alapján adhatja át nekik a CPU-t (bár bonyolultabb ütemezési algoritmusok is léteznek).
  • Adatátvitel (Data Transfer): Hálózati protokollok és adatfolyamok gyakran használnak puffereket, amelyek sorokként működnek, hogy kezeljék az adatcsomagok érkezési és feldolgozási sebessége közötti különbségeket.
  • Szimulációk (Simulations): Valós rendszerek (pl. banki ügyfélszolgálat, gyártósor) szimulációjában a sorok modellezik a várakozó entitásokat.
  • Gráfalgoritmusok (Graph Algorithms): A szélességi bejárás (Breadth-First Search, BFS) algoritmus alapvetően egy sort használ. A BFS egy gráf összes csúcsát és élét szisztematikusan járja be, a kiinduló csúcstól távolság szerint növekvő sorrendben. A bejárás során a még nem látogatott szomszédos csúcsokat a sorba helyezzük, és a sor elejéről vesszük ki a következő feldolgozandó csúcsot. Erről bővebben olvashatsz a szélességi bejárásról szóló Wikipédia oldalon.

Gyakori hibák és félreértések a sor adatszerkezet kezelésénél

Fontos tisztán látni bizonyos alapvető különbségeket és odafigyelni a speciális esetek kezelésére, hogy elkerüljük a hibákat a sorok használatakor.

  • Verem és sor összetévesztése: A verem (stack) a LIFO (Last-In, First-Out) elv alapján működik, azaz az utoljára beérkező elem távozik elsőként. Fontos megkülönböztetni a kettőt, mivel eltérő problémák megoldására alkalmasak.
  • Körkörös tömb telítettségének és ürességének helytelen kezelése: A front és rear mutatók helyes kezelése, különösen a tömb „körbefordulásakor” és az üres/tele állapotok megkülönböztetése kulcsfontosságú. Egy gyakori hiba, hogy a tele és az üres állapotot nem lehet megkülönböztetni, ha csak a front és rear mutatókat használjuk. Ennek elkerülésére vagy egy méret változót vezetünk be, vagy egy üres helyet mindig fenntartunk a tömbben (azaz egy N méretű tömbbe N-1 elemet tárolunk maximum).
  • O(1) komplexitás félreértése: Az O(1) azt jelenti, hogy a művelet ideje konstans, nem pedig azt, hogy azonnali. A konstans idő is lehet hosszabb, mint egy másik konstans idő, de a lényeg, hogy nem nő az adatok mennyiségével.

Sor (Queue) szimuláció: Mit figyelhetünk meg a gyakorlatban?

Egy interaktív szimulációban egy sor adatszerkezet működését vizsgálhatjuk meg, amely egy körkörös tömbben van implementálva. Ez segít vizuálisan megérteni a belső folyamatokat.

  • Elemek beillesztése (enqueue): Amikor új elemet adunk a sorhoz, láthatjuk, ahogy a rear mutató előrehalad a tömbben, és az új elem a rear pozíciójára kerül. Ha a rear eléri a tömb végét, a következő enqueue műveletnél visszaugrik a tömb elejére (0-ás indexre), feltéve, hogy van ott szabad hely.
  • Elemek kivétele (dequeue): Amikor elemet veszünk ki a sorból, a front mutató halad előre, és a front pozícióján lévő elem eltűnik (vagy logikailag üresnek jelölődik). Hasonlóan a rear mutatóhoz, a front is visszaugrik a tömb elejére, ha eléri a végét.
  • Körkörös működés: A legfontosabb megfigyelés a körkörös viselkedés. Láthatjuk, ahogy a front és rear mutatók „körbejárnak” a tömbön, hatékonyan kihasználva a rendelkezésre álló memóriát. Ez megakadályozza, hogy a tömb elején „lyukak” keletkezzenek, és feleslegesen megteljen a tömb, miközben még lenne benne szabad kapacitás.
  • Tele és üres állapotok: A szimulációban megfigyelhetjük, hogyan jelzi a rendszer, ha a sor üres (nincs benne elem), vagy ha tele van (nem lehet több elemet hozzáadni). Ez segít megérteni a front és rear mutatók közötti kapcsolatot ezekben a speciális esetekben.
  • O(1) műveletek: Bár az időkomplexitás közvetlenül nem látható, a szimuláció vizuálisan is megerősíti, hogy a beillesztés és kivétel műveletek mindig csak a front és rear mutatók módosítását és egyetlen tömbelem elérését igénylik, függetlenül a sor méretétől. Ez a konstans idejű működés a körkörös tömb implementációjának egyik fő előnye.

A szimuláció segít vizuálisan megérteni a sor adatszerkezet, különösen a körkörös tömb implementációjának belső működését, és rávilágít arra, hogy miért ez egy hatékony és gyakran használt megoldás a számítástechnikában.

Gyakran Ismételt Kérdések (GYIK) a Sor (Queue) adatszerkezetről

Mi a legfontosabb különbség a sor és a verem között?

A legfontosabb különbség a működési elvükben rejlik. A sor (queue) a FIFO (First-In, First-Out – elsőként be, elsőként ki) elven működik, mint egy hagyományos sorban állás. Ezzel szemben a verem (stack) a LIFO (Last-In, First-Out – utolsóként be, elsőként ki) elvet követi, mint például egy tálcákból álló torony, ahol az utoljára ráhelyezett tálca kerül le először.

Milyen időkomplexitással bírnak a sor alapműveletei?

A sor alapvető műveletei, mint az elemek beillesztése (enqueue) és kivétele (dequeue), ideális esetben O(1) időkomplexitással rendelkeznek. Ez azt jelenti, hogy a műveletek végrehajtásához szükséges idő konstans, és nem függ a sorban lévő elemek számától. Ez teszi őket rendkívül gyorssá és hatékonnyá.

Melyek a sor leggyakoribb implementációs módjai?

A sor adatszerkezet két leggyakoribb implementációs módja a láncolt lista alapú és a tömb alapú megvalósítás. A tömb alapú implementáció speciális, hatékony formája a körkörös tömb (circular array), amely orvosolja az egyszerű tömbös megvalósítás hátrányait azáltal, hogy a tömb végét és elejét „összeköti”, így optimalizálva a memória kihasználását.

Hol találkozhatunk a sor adatszerkezetekkel a mindennapokban vagy az informatikában?

Számos helyen használatosak, például a nyomtatósorokban, ahol a dokumentumok várnak a nyomtatásra; operációs rendszerekben a feladatütemezéshez; adatátviteli protokollokban a pufferek kezeléséhez; szimulációkban a várakozó entitások modellezésére; és gráfalgoritmusokban, mint például a szélességi bejárás (BFS) során.

Practise it, don't just read it

35 flashcards, a test with explanations and a podcast for this topic. The account is free and you pick up right where you left off.

  • No credit card
  • Web and mobile app
  • 330,000+ students
Start for free

Related topics