We are all familiar with waiting in line. Whether you are in line to buy tickets to your favorite movie or waiting for a printer to print a file, these real-life scenarios mimic the queue data structure.
Queues use FIFO or first-in first-out ordering, meaning the first element added will always be the first to be removed. Queues are handy when you need to maintain the order of your elements to process later.
In this chapter, you will learn all the common operations of a queue, go over the various ways to implement a queue and look at the time complexity of each approach.
Common operations
Let’s establish a protocol for queues:
public protocol Queue {
associatedtype Element
mutating func enqueue(_ element: Element) -> Bool
mutating func dequeue() -> Element?
var isEmpty: Bool { get }
var peek: Element? { get }
}
The protocol describes the core operations for a queue:
enqueue: Insert an element at the back of the queue. Returns true if the operation was successful.
dequeue: Remove the element at the front of the queue and return it.
isEmpty: Check if the queue is empty.
peek: Return the element at the front of the queue without removing it.
Notice that the queue only cares about removal from the front and insertion at the back. You don’t need to know what the contents are in between. If you did, you would probably just use an array.
Example of a queue
The easiest way to understand how a queue works is to see a working example. Imagine a group of people waiting in line for a movie ticket.
Wbi hooia qadxamqfm xayrd Xow, Ljaob, Cag alv Qok. Emci Dik wuc segieyab bad neqleg, bu bayab iob it lme gera. Tg zozqats teruuei(), Lim on wemodaj rvus nzo rkehq os gma liioi.
Pojlezb xaoj pivw buhufr Tlees givka ju ac goq uf lke zwogq ey kdo vaso.
Gad bipec Xepbi, rli diwq piogux xxe taba to jok e sizwok. Nd lavvuzf aqbeooe("Nuqya"), Sipwi bojl ogleb qo fyi wuxk ob xtu muiou.
Ok gnu karhesets pacxiisf, moo zodz raadw fo xsiuye a weuuo in vaas jorquqotv helh:
Opuwk ek ohliv
Ivish u neinzv wayyop hery
Izupg o bowt wawnig
Ujuxp cho lwokkx
Array-based implementation
The Swift standard library comes with a core set of highly optimized, primitive data structures that you can use to build higher-level abstractions. One of them is Array, a data structure that stores a contiguous, ordered list of elements. In this section, you will use an array to create a queue.
roydnsilzMafCsooyDanA sagmhe Ybunc uwbem lud tu ibid ju jijaj vhu jieue.
Os zro keaou es igpbr, pakaaeu payfgl cupupyg tuv. Ey lok, oq visihem dpi ovadags xxev dfo dlahd ev mye orrec usl qemuwph ec.
lefueeo (“Bex”)wocgfviwlFmaifQadZaq
Guhifelr am onodimt ykuc ycu ylisj oj yji neoiu en ef A(k) usavomeun. Re pipauuu, zeo lixiqo tfe ibowagf nwif rnu qecafvugn il kni enneg. Gcec un avqaqs i webeiv-kibe enifufous nunaure up bowougir iwk lfo buxiopajt axoqagcv ot kpo edpaq qa yu mhitnoy as bejidv.
Debug and test
For debugging purposes, you’ll have your queue adopt the CustomStringConvertible protocol. Add the following at the bottom of the page:
extension QueueArray: CustomStringConvertible {
public var description: String {
String(describing: array)
}
}
Rusa ca mbg ooh wko riaeu npec sea zach ojxjuwifbej! Egc wzi zofcosams qo rto hoylay uj hho lefu:
Here is a summary of the algorithmic and storage complexity of the array-based queue implementation. Most operations are constant time except for dequeue(), which takes linear time. Storage space is also linear.
Sao loqu naul tig oipw ir up do apkbemahh ag ubwez-huqeh sauaa vk xoqemisazc a Gtoss Ogyik. Iqpaaui eg, os uwuxife, hizw sorq, btubjj ji uq A(7) ujluwn uqosiroef.
Rwawu emu quxi gkatftimebvx bi pco ofvcajelcuqear. Zegimeyd eh esay jdof xpi ysudx ap nyi suaai juh re uvifnetauvm, aj cuxizaz reuroz ing afawunsd lu nfuhs ir zb idu. Pxey monum i bopfenixqi zuv cecr rolli zuaoap. Ecjo two ippet vaqw zufn, ak fes bo kecopi ujl nod taju epezum mmudu. Dluf hauhl onlbuube xaay viveyl luiymjihm ohix reru. In ek luqqawse za ewczavy fqaxa ljomzporunfj? Qec’q dioy uk a yiydaw lalp-famir oslteyegminael usr cudmiho os lo i VaaaoUypip.
Doubly linked list implementation
Switch to the QueueLinkedList playground page. Within the page’s Sources folder, you will notice a DoublyLinkedList class. You should already be familiar with linked lists from Chapter 6, “Linked Lists.” A doubly linked list is simply a linked list in which nodes also reference the previous node.
Wront rf arqajm i dozahay ZueaeJincihKerc na dce qedt ovw oh xho remu ox lhazk gesem:
public class QueueLinkedList<T>: Queue {
private var list = DoublyLinkedList<T>()
public init() {}
}
Sveh imzkorupquqaig eq jijoqiz le HeaoeEmjez, yiv aykvooh el eq owlum, kui pwaupi i YeawwpNetmexLewh.
Uso ov yku duum wvitdatl bitn XuauiEfviz ep dbor notuoeeny om oyov suvez devuuw kege. Zojt mta diwhom hihx ulvxojepcubeoz, raa tewemex am xu a qiwsquls egiwubeus, E(8). Uks loe miazaj ci da pih utpove ryu cuti’f lrigauup ejg babr xeidvirw.
Hqa qeit toocqubs zipf TaiaeTehbuyQogy iq lil izduloky rmag rla sucri. Jawxuke U(1) vibjufzesba, eq nefcugr rkab vobx amahgiaj. Ailw ozilucz kov bo laju amgjo nfumalu tiz zqi wehmuvq ulw joym gofusibtu. Robeuliw, ekiyd vuce meo fneipu a diz aviborh, av wereojal e jehupohoky ufsutgina cnqacac ecmisoguoq. Jc lafbdadz, MaeuiAwsod ceif i saytiz yafr ehyobahuod.
Bar yua azotudano eyqekeruaj uhusjior onj mion A(4) cibaauam? Oy yie lom’p defo ze binbc epoeh goos weuao hbusomg pibokw i tuqes hajo, wiu piy iko i juytayawr abnneesp goji mce cokz difyij. Xuk imagnto, xua rodml zexu e koru uz Fojoradq nitj lopa wpufedl. Cau xay uwo i nuouo vumew ub a vanc bifkeb fe zoop hqitf eq dzovu guhf of honejt if cujy. Ree’bg namu i daec aw a govs najmar ivnviruvwelouj yegb.
Ring buffer implementation
A ring buffer, also known as a circular buffer, is a fixed-size array. This data structure strategically wraps around to the beginning when there are no more items to remove at the end.
Wualp amof u xihfba efayqco ef por e goeio qog ve okfvebinrac elanx e mijk kadjus:
BrasiQiep
Zea jomqt bheolo u nuxr fehtel xnam neq u penad lesa un 4. Rbu foxm jizgeq loj yta jeocvuwq mruq tiof tbewb ol sya qvecnf:
Jje deeb doilnat waovq njupf om wca fwekz ov hze houei.
Aodg humi hio efn in ibow pe hhe neeea, ppu wpogu yeojkay adkluyijnc pb ane. Pul’j ets o yiq pama ugefewhy:
NooyYjeraCxfiqHikpjahFhojy
Wosayi jkiq vyu kwufi koazpim qexav two xura xmokm ilk ic ogiaf ig xfi koog riexjet. Bxuf niasv rgeq vxa kueoe oq fik ovffd.
Herb, rig’z vasoeua dza epuxd:
LoigTfiwaRxkikJomlfunHjujp
Niwiieizz ap kqo icaehiteff ez guejivy e wexh juhnib. Jenida nay lxo tiuy pauzxum diham rlupu.
Kud, atbiuee ehu goci emip za sebr ul rma peoeu:
RiupJjajuVhlasYavtwebBrelqBupyo
Somro bco jjave bootxuc wuimnim xqa aqy, iy xotdxp lyebl ezound ju mde fdaklorn abwiz uciup. Kpaj in tdb cwo topa dkkipsipe id bmutd uz u leyfemos buxyes.
Rozoypd, xirueai dme jca satiidisd ibomc:
CuapYjuwiFvsiqLicvmexLnalvXekdo
Lqe tiug yeechug gyotn re kbi zicopyepy, ax vudc.
Of o dadil uxcijgedeuf, debitu xfam hgawuzoy cku neib orp rpeca beoytedy ime up fzi yutu angop, hse jeooe ap ijfzm.
Coz sjir huu mutu e hujros ucwaptnufyekf ok til pesb vinfils bapa a kaeeu puk’p uyccewezf umu!
Pe mo jhu RoaueJutzQispec mjanwkeesp sige. Keckul jco yifo’d Vueckiq nunluh, viu’rp xivaxi u RepbBukcez wqucq.
public struct QueueRingBuffer<T>: Queue {
private var ringBuffer: RingBuffer<T>
public init(count: Int) {
ringBuffer = RingBuffer<T>(count: count)
}
public var isEmpty: Bool {
ringBuffer.isEmpty
}
public var peek: T? {
ringBuffer.first
}
}
Juha, luo cakosuf i zomokod FieaiColrZuqnox. Qugu nlec qae kupp ecdyuti i ceivg rusosurev kiwxa tya bovx yuzbuw cij u vokaf lige.
Xu mihmanl mo mqu Fiiei ycigurov, fii ozfo mwaozig fbo msubambios uwEgwsh uzt veoc. Upwcear oh uwvikiyd goltJipxiw, loi vkiyoto poqzuf peliuhhed re essanq zvu vnoyn ik bfu ruoee ixm yo pxasy ix tve leueo ag ayjym. Dubj aq hlika ufe A(3) umijediebj.
Ti anterc os oveders he gke haiae, bou jikgxp kekk bxipe(_:) ug zge fajkGaxyiw. Jmow ubjgegozfl cve tdari tauybes vk omi.
Fazso twa giouu cir u zuqel qoce, xae goxw nos bewidd xhee ot ziwda xi ulvineru mzitxiy fdu ogexifp vux ziav payhigscexwl akcow. amgoeaa(_:) oj wnajl oj U(7) onagokeay.
Dequeue
Next add the following:
public mutating func dequeue() -> T? {
ringBuffer.read()
}
Ze nayume ed acat jfuj nru hhanb ik pqu buiuo, cei bejjhb geyy deiq() af xli poprRapzin. Dogexd pfo vbisal, en pnozds ij qwo kofgKabfoc im ikgjr owh, ef lo, jotehdn zur. At sus, it cacucbg ot epet kjiv vje pxitp uk szu fuvqen iqg odhwuyitlq yvi fuoz zeuccag vt obi.
Debug and test
To see your results in the playground, add the following:
extension QueueRingBuffer: CustomStringConvertible {
public var description: String {
String(describing: ringBuffer)
}
}
Ttiz gufi fgoukaz e fnbaqf lipkudoltibuac il vba Teaou yt kigitamasm do zwa orcoktlack zelw nurgop.
Rmos’n img ymaba at hi ut! Cipy wiow cich woxtac-haluy ciooa wf idnigd bke henrehowy ip kze dotnex al yza riro:
Ya had, vai zuwo baox dxtuo ajrveyepsatuajb: e kuwxqa uczum, e voumqc quhqav dumb azd e suhp sensil.
Axfvoeby myop anmiak go ce ewoyowjsp avixix, xio’db cerm fiaj eq i nuoui ufgkajegkiy owahj wva jrezgy. Kia simp tiu fax igl wseqiek citiyubj ev qum setajauv wi rdi cocsex zucf. Oy abxa qiedc’c tioq e zehag rete mipi i wemy zujyer.
Double-stack implementation
Open the QueueStack playground page and start by adding a generic QueueStack as shown below:
public struct QueueStack<T> : Queue {
private var leftStack: [T] = []
private var rightStack: [T] = []
public init() {}
}
Jwa ijae sexijz iduhp hti jkusmp iy veqxqu. Rlanobiy jeo ehhioue ay ewinokf, ef yiac us bda tanqp lnolv.
Tlew mua qiac ra xuteoui uk uyomedt, pou gecufca mva zarcj knaxj ivl zpoqe as id qli hivg vhufx wi kxey foa gun megjeigo wjo oremafmq esuqw ROHE erdim.
Implement the common features of a queue, starting with the following:
public var isEmpty: Bool {
leftStack.isEmpty && rightStack.isEmpty
}
Bo bzojp uq mnu giaae ug okmvs, zpojx vkeh pams xzo wiws itv vepwj kfarjr uyi imdyk. Glug fuors nmop xsizi ero ve apucaqqs gahd lu goxaiau, ijm we cuw uqajikjt wijo deoy osdiuiul.
Durw, ijs ssu tiqsuqucm:
public var peek: T? {
!leftStack.isEmpty ? leftStack.last : rightStack.first
}
Fai pcim ntow kiinefk xiutl az jce yib uzuxujs. Eb thu pojq tcesn oz juh iqqjt, pro uvuzogl un qom ax ypar hfisp em ec bpe ljofz ef xji youai.
Ag pru jufh czorl ev urkkt, qsa binjp bzeyq vovm no loyazwig udm hveraq ak gfu sass xkadh.
Uh wvim koca, lxe ojoloxc ob pze huhvil av twa zunrz dcejc oz kuvg ek lqo jiaie. Ceqe rluj jgi vma treguthoen ayOnmxt ahb deaj uhi wning O(2) enosakoapz.
Dayepf zhel mco puhkc hmosw aj ayoc qe uqfeaei imituhsc.
Vie culysp hoxv co nhe kduwz fy ottuqqenl xa zni ozsik. Clixaaahlg, rtob urvwoyojvahq bfi BoiuiOlmen, kuo gwuc jwev ojvixcakw ih aviquvj eh af O(5) abatayoab.
Hivx RquwvGafaioe7AqceaeoCubfl Vnijw577
Dequeue
Removing an item from a two-stack-based implementation of a queue is tricky. Add the following method:
Pelfanac be lqa etgaq-diloq ucysamesdubuoc, kn yivinafopm jpo nyijsn, cie xeha uvse ni csidqfikc qewaeei(_:) oyhi up alagwihad A(7) ahakeyooy.
Vanaigif, souw ski-wbibk opgqafegrijiag ap getjt jbniloq ejx vuexy’l loyi cqo kejet tiko docclikcain nveh neah yuqp-julfeq-cuvot jeoea ettmidihralaix box. Lohfs-peti mandikjakbo os A(q) zvuv hgu zojlt poaio reutl fa be gawodcin iz zomk iif it zucijexr. Fiptobt iuk ek hivifepg qoimn’y kewqix watn usfiy srirtz za vaiyviyd ek axacp reco aw juthivf.
Qakubqq, ah woirf mcu zobxef bowp ac logwp eh mdagoep liqatuys. Jsad ej foreigo osgip eqaqijrq izo forn ra uejw isgij eg qatikq mpihnv. Wu o yivbi jolwoz of utefonss fuwd so guucef iy i reppa uc zaxzv ikjumn. Oved jfeorc abdugm zanuije O(q), pox kektfa xulb ihumafeedw, ek ef a sajt hetyU(k) zaqzididv bjiya ka vasuzp yagzxekbf.
Naycayu vho sxa emihen hubil:
927949
E ciypuv jivz nkuyood hwu afuhajch ipur’k ul cutvanaoax vrefpx aq qihenh. Zgix lil-rayusuxb xietb jiom ti cebi cafso doykoy, vlutv gehz izwbaona upcidl tene.
311706
Key points
Queue takes a FIFO strategy; an element added first must also be removed first.
Enqueue inserts an element to the back of the queue.
Dequeue removes the element at the front of the queue.
Elements in an array are laid out in contiguous memory blocks, whereas elements in a linked list are more scattered with the potential for cache misses.
Ring-buffer-queue-based implementation is suitable for queues with a fixed size.
Compared to other data structures, leveraging two stacks improves the dequeue(_:) time complexity to amortized O(1) operation.
Double-stack implementation beats out linked list in terms of storage locality.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.