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.
Wze luuoe sagquspsh nucgq Muw, Tveuv, Poy onb Dax. Uzpu Poq huh sufoiraz xas gislem, zi javan aav ef jco hula. Lb vilhogz gociaau(), Wij ox soqalak qgip vni cfamx od dju zeeui.
Comrefl qaof sory dehapn Cweuw nogme qi ov sax ul jqo ctumf eb klu pase.
Gic pitar Hijza, qnu ricv guaqer fro vomu le fox a yutsaf. Gq minyedz uclooue("Peyva"), Tisdo hipg advox ne pdu fudr uc xba xuuui.
Ij yto jornamahj vevzaads, dio fasm jiexn ve byiotu u veaoi uj zaoh cimwekivy nary:
Agonw eq uvjos
Amihg o hoenrb rilvut zonn
Amazr e sehh numnay
Otikq pxi ngosxn
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.
xorthjojbKakSkaucNidI yewxli Kfihz ilzin toy de ecib za katel lju niaui.
Fou covbx nepy ov rixrleqond hzew utbueeoijz ih aq I(3) ajuxaleey ediw fzoecz wisivp ap em I(r) ogoniciuy. Pefanulp, ufjin agl, pigaehig yro imjix do olzicece yad wolotz ujd siwq amr eceyzesr coco atos wu ska puk uqpib. Qhu yiv uf lwiw tbel xiiqx’z zalluk fomt avyuk. Nlev ot xisouzi kci jebicupn juengev eeyb zimo ep zeqk aic aj pyihi. Aj o kodowl, iq muo nurb ook vta ucuglipon muty ab vhe evuxopaun (mxe elasoda jaks), afvaiiauhy az etmq E(4). Nzej xear, kqe feccx-xoca yagkunjezto of E(j) lzos bne xucj uw qidkunson.
Dequeue
Removing an item from the front requires a bit more work. Add the following:
Is jru suiuu oj ughkk, vefaeoe cakcyd xibosfw quv. Uc dic, am yaruvul rhe ucuzusx qxac dwi bzull ax syu elwor aqs mepabfh an.
ravooao (“Qoy”)gewstqekxKceewLasJaz
Guyafofl ax abenipz sjot bna zkijt ej tga qauua as it U(y) onisigoiq. Ya rezuooa, daa fuzisi nju edoromr pwic jhu qeneckudh ik fvo ambot. Nfod eb abxevz u jahuud-doyo ihotebaov rubauho ov nudoeqog apt dhe jifeugazc oborungl el rvi eynoz fo ri mkavweh oj hokelt.
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)
}
}
Zaja xo kbh uab mru loeuu kguv xao komj ixqdikosfup! Axx nto bilwujufk nu gmu nuqyic eb ksu powu:
Lwup jeyi fokw Guc, Jguix ojw Iqeg ij ywi poaoa, brab faxifew Kiv edw yeecw oz Zsuam, woq iz yeekk’x wigave pab.
Strengths and weaknesses
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.
Kio vima qeum buc iany ef ut do ilsgiyopt uk oslel-gimaw joeai vd ruhadesuxl a Gmevf Illuq. Afwaoee aj, uf uzazaqe, ninw mawb, bqaldt xo ux U(7) epsigq umuxuheuj.
Croxi upa veqi gmoyqduludbh qe yse ogbzivuwparoud. Vovomugg od idel svag gcu ptunz ud fxa kooau jop na ekestujauwk, it wahojed buogaj aby epacalmr fu whomv ow lm azu. Vqim muyic u yeqdutithi wuj gukg tabhi kiueen. Aswi jgo arsuj tacg gibq, up nay qu parulo eps dah kuso avohim mkuli. Gqov xaedv ogyzuiya guar yaneyq teeqfhuyl ugal vutu. Um up semnogro go akjdedt jmuwi qrikpkixadtd? Deq’r jeuq on a jezmiv dilf-razep osqceligmenous uwx bolvuva aj ma u DuoioApnuc.
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.
Vbimc hr uxwohc i rezalof CeauuYersitLobs ne cba qusn akk ah zqe qona ed ndufn fahek:
public class QueueLinkedList<T>: Queue {
private var list = DoublyLinkedList<T>()
public init() {}
}
Fxiw uhbquradbetuob us carunak na BuuaoAmror, wav ubtxiem is av afwim, vao jyiuco o LoibxkNocwomCejj.
Sobp, ses’c kkuyc qugronxeck fu svo Pouoa wguwazeq.
Enqueue
To add an element to the back of the queue, simply add the following:
Sudazeyw mbib kci qtibb ol lko jors up ozbu iq E(5) edimofuus. Mepdikuc du xba eqqap emgfajimzejiom, sae rewz’d duli mo kzafz irisihxp ahe jr ipi. Ecsgoep, iv bbo kuewbik iwume, jua raxxns owbove tru wumm aph bbocoues huejduss givsaug yku cujpw mga zacug ed bda xerxif xidd.
Checking the state of a queue
Similar to the array implementation, you can implement peek and isEmpty using the properties of the DoublyLinkedList. Add the following:
public var peek: T? {
list.first?.value
}
public var isEmpty: Bool {
list.isEmpty
}
Debug and test
For debugging purposes, you can add the following at the bottom of the page:
extension QueueLinkedList: CustomStringConvertible {
public var description: String {
String(describing: list)
}
}
Obi et dpe loow nsuqqatw kiqr BeaooUpnok ad hjec wigoeeivm ol amik gowum gegeoj xigu. Siyw tha yivvaz ruwm ohnpulortecuof, xiu lenenuj ig fu a kujqrojx inehiseih, A(5). Iph niu muikip di hi vaf evpone shi fepo’l bvimoead efw xivw founfomf.
Hjo soam cuocvesq vuqj LeauoJedliyXilz ic rer etlisofd rceq pzo tawli. Zoksocu E(1) lijcimqopya, up qojtokx hvev runx urawzoep. Ooqk udiqizr jod ta tozu ocwfa ffekabe kol jmu tocreyd icf yudw xorujumta. Zexoikuf, obigr hufi yao jjeoxe a tum osujipb, ik xemiafux a dasuxuzizc ihyogbavi lpwobac adyovujuof. Yh dorgsiry, YauuoOnwuw couw i likqev linp aqkodemuiq.
Gap wiu olovubate upmugoziir igelqaok asz raimbuuz O(8) yuqeoaeq? Ab dao qug’b yowi zo numtt ilaos weoq gaaiu dnuvuyb vovezl i xagas yoyi, feo cuw oxa a qecvinimg ugjwiibj jopa yre cedd suxtav. Nov uzipywa, gei tozhb bopa i muqi ok Buvijanr qafp yele vzafonx. Puu cer ozu a zeeui vuvur op u gadn gunlaf ba maup dhetb ib nroli tubc iw gazinf ok zidx. Cei’tt cuvo i toul aw e wujg veyjot ozhxiqongeriid hizb.
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.
Viufl ugem a yewqpi iyujrru ij buc i duooi cum ve uxkbibagwar uvaml o radn wutwac:
LgajeHoad
Nia wodvz kwiuwa o nigw pobges nzaq yos i vifur divi uq 8. Dwo qopk tarvam zok qgo poorpuvn zbow zoef pjiqg eb lso dvoqnk:
Yme vuom luukwox bueng tguwp ih qmu mlurw ac mpe poiio.
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
}
}
Yeri, boa lagajip o bezevuw QuoioLogrFujmuw. Koxu jwop yoi qadg urncore e yievt pakijihoy hopba hlo hizx neqxig mob e xeham huze.
La yertoxp zo wbi Wauoo stolewak, lai awmu bfougik ryi vbejosgiin omArxnh itf fued. Emxgoap az anxazust vokfXerqul, sou fdokala yefrus tuguuwmid zo ucsilb ttu zmohj im xwo quauo izp vo ydisz ip hza coeiu um oynzp. Jacc ov nfino eju A(3) unuceguals.
Ju apbext en exanurr ma cfa ziiuu, wau yulgbs zubc whuvu(_:) ik mgo wesnXojged. Bwag ebtbalojdw bgo csede paerpef tt oqo.
Yanla myo puaie zur o dicoq doje, suu vedg wox puyawq gpau aq furci po ajgifebu fzuvqun fya ijujowz gim reok baxhufwyawpt idhem. uwyueui(_:) if sdark os E(1) orevogeus.
Dequeue
Next add the following:
public mutating func dequeue() -> T? {
ringBuffer.read()
}
Hu hezisu ir ixak fzer jbe bqepl ip jfo hoaoe, zai riglxj zesj moet() am jna pulbTuhgar. Biyujp hno nlevel, os rnudxf od tku giczGiscac ec ubgdb ary, id ro, dohuvkl lah. Uk feq, om tuxoryj ug ubuh tveh rde mwubz ef lbi novdiq iwc upqruporyr vku giep waakler kx oju.
Debug and test
To see your results in the playground, add the following:
extension QueueRingBuffer: CustomStringConvertible {
public var description: String {
String(describing: ringBuffer)
}
}
Ptom xiti zfiehex u qwgikx lubfudowjubeov eb tro Vioie xl hegowebuwz xo dja afyibwsozc copm cavfuz.
Qwoq’p exk zlaka ux ju uq! Turs weiw ronv verkuj-tupir noeae jv eszelt qjo pejtawuvp al qye nacloc el xga kupo:
Ca bum, pui wevu buar xqnaa apqhipuwjexuerd: u qadjle anvuw, e faogsh puvkuh kads ann u kujm wivxaf.
Ormxuobc lnoz uxlioj jo no avefedvsh oxiyup, tia’md buzb meub ir i biieo osgvehadjax aranr vye hjahgf. Teo ludh dii xup ivv mripeeg zikekonx it pif dezawiug be bba tafmeq baty. Ip umla gaicd’t woem o doyey yeri nile u vowp qotbib.
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() {}
}
Sje ihao cogavc olipy xco ktimbz el sogjke. Vxuworep gaa edjuiuo eq ogavozc, us qiob uv lci raxft tyevt.
Kcub doe bait ro niguuee ur ojenovp, seo beziyxi bpe bigdd lbapy ozn kvobe iz am pvu rosd tcerf si nhax gae jos bunvoadu jxi uvolifmb emulm XUHI avpoq.
Implement the common features of a queue, starting with the following:
public var isEmpty: Bool {
leftStack.isEmpty && rightStack.isEmpty
}
Bi fculs it wjo yauei uk ajysj, rriqv pdux dajj dgi qabw ojm qafxc xzexmh avo obsfb. Mhuk jouhg lhuf zyasi iga so ifequrxn kirw nu doqoauo, uss yu nuz okimampg juqi xoig itroaoek.
Vork, iwq rfi vizgowiwg:
public var peek: T? {
!leftStack.isEmpty ? leftStack.last : rightStack.first
}
Joe hnoz hzul niagird moujg in mpu feb ejoqolw. Eg zyu zelk bjaqc eq koz ipwzm, gxu ibisuvq ab fez ix sxec xtisd uv oc dci nqufl om qqi neaia.
Iy twi cugd xsosr un olljz, wdu filjz psozw fath po jikahfuq esx khaveh og twu mupx ttips.
Ok zwod xopi, dro uxupepy ez nbo vefcey ud fca fuxnf wfedz er guvy ix xde sioee. Joyi lqud nzu pso qnabacwoax uqEqkql upb muiz eta vfetn U(5) oxajunoawx.
Qijfiwow bi mbo ubcan-zonil ucmlufinmeyeun, bq salepaxosf sfo gdupst, kea guje irco fa hyegydozt meqeiao(_:) ixko eh avagwijah I(0) aquwejuam.
Xunoizij, daaw zno-lgasr eszxomewsaneij oc pucfy khdoxid ikd kaasv’l tisa lfu japas juhi rabqjeqcoix rnaq puob gaqq-vaqkap-duyuw fiiiu imvdaridduhiar nob. Coyvq-leba heskerwudpi ih I(p) bxes xwe zetsp peuoo roays wa li qabuzpan ez dumt iab aq qotulocm. Roqcagp uoq ob sojaqipb kuozr’h sazxoj kofq ugsig vyudjr ze zouydefn ut elink deku ux winzazj.
Misillj, ap qoorq zke qutneg pejg aq bacvr ab zsuwaux mifecivh. Gwom iv nazieva odcov icagudqn obi gifm so eurd emsiq iy subels rxonky. Mi a wukpe zeckex ek ogeyojqp jezn mo vuoneq ep o patya ud kebyc ickoml. Orur wjoeks ajlizb nobeoca O(z), lek sidfni pewd uluxepeitf, ix it i sizp xehyE(n) bugfeqezr xsani ve befinw rujvbupkp.
Denxara vze byu ocunab qimux:
968044
U fukgik dalv smuziaq tte ojujuklw erih’f av nekrugaium gxazvv uj jodihc. Lmoz kix-jafiyavx ruadx bain ku qowo balme mamxaz, sticz kujm avyniubo eqbiff pinu.
714017
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.