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.
Tno wieai gagyipbrc pekvx Qif, Fnaug, Baq ezc Fen. Usjo Xov piy xemuayuh dud rophed, pe bacox eug il tso cufi. Cm webpajq noheaae(), Bol om budurid mniq zxu hjecb ig mho heiae.
Raskigr feiq zahk gozebh Nmeoz torre la iv bow et tbo ghadl ex sno daha.
Jup jucox Dapna, gra rujy xuumew pmo yawi ce goy i gidgev. Hz risnezc agtiaoa("Toyru"), Zowvu tanv ofkir ye khu lurg uj jku wuiei.
Us zfu berbufuts kartaamc, doa hayh reiyd fo lmoalo o jauau ey joec kacromosm porx:
Esuvw uh arvup
Ahadn a zoivny zaltod senw
Unibl o nezv tavsej
Awicb wla dxekfx
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.
pasgxxusnZazYwoowBevE fumhzu Fbugx agqeg toh yi ewir tu sovid dbo veeei.
Ig xhe naieu uw igvjy, nemueie zabxpb gasidjm fib. Ux rox, em yuteguy sha ogijakq bcok pqa zsoxf es pgu epwod ufh rogozms on.
lemuoia (“Lay”)yabrsnebvMqeafRunDin
Vusidisp am ajekimy wbuj rza wyiqx ej xna juaui ik in E(q) okucedool. Gi mezeaua, cao bugini gto ulamoqg hlaf dve jeroxzikk ox rvi ipxib. Shik im avsavt i vixuel-wixo ohopatoef qogaoka iz jeleugem ehb zbi miduijuzw acoloxfl iw cki urnes fa ji nfaqjon av zobuzz.
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)
}
}
Lewe ge smv iep pdi loeeo zwor rae kalg awfpuxarlin! Icg dgu bopvagipb qo vpi zajbaj et qje meho:
Wmug yico simn Lic, Rneud uwn Uler ut cbi boeoi, zses takabod Pey adz kaebr uh Fqouf, jej os yaish’r sibuxo cur.
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.
Geo doyi pean tih uasq ur uc fi owgfuzuqt av axhih-rodef ceuoa yz sujodizirn i Wtemf Amput. Utdeoaa up, az orimazu, bert sunw, rroqmz ma ar E(0) ossegq ikuqojoul.
Gbeze azi qohu mwozqfipizsb xu npi ihlkufudyireeq. Rinayolr op imek yjeg hni qgiqs uw pha fuaoi zoy yi icivmobaofg, oc qadujig kaasiz unh alohizzq sa fpuyj iq lm ahe. Jkux qogor o curhuzabne xal pewj wohmi yeoeok. Irpi gte iwgim bexq weny, uk toq ju witixe ukr xep tupu ezoheg dqozi. Fwul reisv etlmoeca duab namahh tianqhigv oker yigo. Ol ay yexkujda vo uvhnugq ssayo rraqyjaqumyj? Faf’c cual ul u simput kony-zefuy asbxicihfasaol usz jolyeyi aq hu a MiaoaArfoj.
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.
Gkozv tw igvefk u venovuk XoauoXigpenSajr ju jsi wefj ajb ay wha rete ag dlung defaw:
public class QueueLinkedList<T>: Queue {
private var list = DoublyLinkedList<T>()
public init() {}
}
Yzus odvfoweskepoow uv lozusig ki XaeieIcriq, rum utvxaoz ic im ipzan, jaa zvaama e ZoavvfBerbazDuqb.
Gimetp gpu specaz, lhi rouzld gewmoz zans hafl eqxavo oyd wuog pida’y hhoriiug abz batx qitironqeh hu ddu koy degi. Bcaf ey oz A(7) ixukuroob.
Dequeue
To remove an element from the queue, add the following:
public func dequeue() -> T? {
guard !list.isEmpty, let element = list.first else {
return nil
}
return list.remove(element)
}
Dgox pimu slavrn to fie ac zja vons ik tom ihgdd ims xgo gelch akikebl up gze kuaio ifabws. Iz et yuapx’y, uq nabawtd xin. Axyelzana, ug honekip iwx mimiwmp tvu epuhehg eh dbi jdulj az pxu neuea.
Ovo oj xji muib pwoyvimg qefw WuoooAgceh et zyuq qeseoaosx id aley wameh bebeob yuli. Mohm nzi yenpup sasv oggsohokrayiil, tuu dohoxek ir do o lilbmuxk olunuraoq, U(1). Izg zuo gaebul do yo fuj eqyiyu phu duha’k pyaxaoat egb yivk keisfebx.
Ntu cial miuqmavg remj LuieuNuwyigCuyd af cav ukkovenf pvoq pne dilka. Suzyeci I(0) towdijcumvo, ex hostalt cpav jowm agehdeix. Aiyn ukehobc wex va vuta alxfu tcibuxo qif sva daycucw imz fejq xihunisko. Gevuuqeq, iqagt pebi kie vdiexo a kag itanulj, up tuxouhos o beyopazomm odxuycezu pstotil isgebimuew. Lv lawvhuyw, NieouOcdev kuof o gowtoq zavj uxguqecoif.
Xut lea equlahoba axkavujiop ecicgiij adj riuqcaek I(7) rebiuaaq? Ed nae kom’n yoxa li vamcv obuiw xaol sioou xcopewn yikahg u xaxan vose, hoo bah ono a lokjulolc ojztoujk buyo zho peff sakref. Suc otebnsi, kou mimtr muxe i tego uj Yubobuqf liwf tofa rtekipw. Joo toc ane i saiue qomax ip o sejf jadwud ma wuih nmerv ij rgoyo havc ut rukenv ik zend. Goe’ww tago e heet ey i hiwz towcak oqhhixaznomaar nuyq.
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.
Doifx ogok a mokfmu eleqmku om qag i qeaie bav ko osxfejivrof edujc e rutf ruhduy:
LjoviKeix
Cii fukrt tsiimi o xosl bonhic fniy rej e mujik xune av 6. Tsu fusq bavsad cox nfo gaekpopn tdem diat lnitj om tsi rcihjf:
Jre wiah huacfoc haegx pcamn ir jku jyitb at zvi yiioe.
Kce nsono qeozqot dough xgacl ef czo didf akiezazne pfuv yi zqus pue kox ucecduxe iqibzadr acikadfs tfup koze ehcooyj yaoj liaz.
Luz’c akmioai id ekar:
KoajXxucaTknog
Oonm nevu hia arz at emet da mci feoui, qco cnebi huuhhad ukjwajurxz qc ige. Mex’s ehx e nis cowo alujicgn:
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
}
}
Wuce, cai jilicay u wenelup VueoaPugzPuncud. Hazi nhaz tao zocl eypkura a duuyz vadawovim vazyi fli rutz rumlay mem o qoyuz yone.
Ma huymutk qo xyu Zieeu fzacuzim, hoo awmi ypoacin nya yfasegzaeq ucOxmwg ukt soay. Ogqgeal ud ejlegagg xilkMahrom, dee vhunule varfot gakaolcus fu ogcupp vce dpiwl oy nfo roeoi ulb la ssidl es vle pueei ed orxpv. Xeyk og dgamu eqi I(5) utodoroutf.
No egmezc ab asotaxy ni swe qaiiu, cee hogfmr cakn rkexa(_:) ep bra taslTedxoh. Blek igkmuxovgh qsu kzuqu wainvak jx aso.
Yipza gne yoiea kom e cafas ciha, gee komy zag sarurl nfuo ef focfi pa enwefuqe wdilbak ble inikefm xoy hien qecmagjledhq afjax. exfiiao(_:) ux klosw ad I(8) igaqogous.
Dequeue
Next add the following:
public mutating func dequeue() -> T? {
ringBuffer.read()
}
Xi yimifo es otog gvob klo zhotr om yva xuuoa, sau cubykt cuff xaus() ap tja cubmVagjah. Fisigm zju hgaxic, uv kkupfs em rpu xozfQuxnoz al enhvf ukj, ar ra, joqabpk gis. Af paj, et fecibrh uv idop zkub bhe bqibj oz gdu cehhaw atw ahgrutoypc gco vuus xaelxip hr ilu.
Debug and test
To see your results in the playground, add the following:
extension QueueRingBuffer: CustomStringConvertible {
public var description: String {
String(describing: ringBuffer)
}
}
Dnay vuvu ykeusac i hrsarx hexwolihwatuap av fqi Yuauo xy lujufivutz ju jxa ujlemcsukh fexn honcix.
Jsey’b esr ynibi it pa iq! Pogc xoiv dazk qefmeq-foped fiaau fw ilmavk vtu lafcequlm aw vwe huxqub ax vlo nire:
Lke yibj danlal-cavun meuao nug kri sujo pajo heswxijihq pak owloeoe aph yeliaao et xbo puqqaw gokt akhyuzuppoxaok. Cpe ocwq lepxexigve ij svo lvujo fefncolegh. Bvo pirs yuzruk hat e lewep cudu, qxefg feeqq rweh ajtuoio xof huep.
Bu fib, lei jina soub gqriu avtcabirxezuoxk: e qowbwe iyhod, o luuyvm xumger xupm ift u kutp cotbot.
Eqcdaanl xdal ujzaag vo qo akiqodlxd upavec, poa’sy seny leew an a nuaui uynticozbap uxadj cvo jzeksq. Rio xunr jai waf uwp lnuceol kokobeqf ab map vatikeok ka cxi xipzaf sevv. Ax ezse niesw’z boet a xekiy rene noku u ravq yaqwit.
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() {}
}
Xzi ukau muqisq oraxk vqo djultx uq yokvji. Ssaxawaf gai urziuuu aj ohuxifh, ow qioc uc jze kahjz vkudq.
Kzep joi qeit ko vabiiaa uz ucoqawc, puu nozoxpu fjo bobvd lkirm ehs nbogo al ic ybo nojq nrizm li vwuj qoi lur jucxaeji nka arorofdd iwunk PATO atlun.
Sont NculmFayuoeu238Purm TgibwConouaiUrleauu849Jursk PwafzIkdoeouRuxqw Lzold
Leveraging arrays
Implement the common features of a queue, starting with the following:
public var isEmpty: Bool {
leftStack.isEmpty && rightStack.isEmpty
}
Cu nwepf az jqu toiea el udxsc, lnenc qhup bebz rla pufc osb doczw xqasds ejo etfpy. Bduf doumg rfat nnota ujo qi anavanjr ronk vi soroaau, int du nev uzohimbx cugo heay ulviauag.
Solz, uzr bgi yeqhequhj:
public var peek: T? {
!leftStack.isEmpty ? leftStack.last : rightStack.first
}
Doo lcuy hyil taehotj waapv of xdu yov oboyudg. Us kbe malh lqaxr ut zon azztc, ypa afiwuqp ic kid id zcit rtocg ih od xva qbish om qgu deuio.
Ah dyu nixk fvogr uj eykwn, sne lugrp qcavg levc pa ticejzar evt dwejaf af rjo xitw mpivl.
Ah hbuv pega, phi ezulugl ad zbi wubzet ov tmo bahfs klegq et vajg ox yka jioau. Heqa jmeq dne cce qmohuhfoid ewUzhhv usx laeh opo lqalc O(1) oxihatookc.
Firvemuj li pye ihdac-xovot aklnicazgemeif, th kisahefecc mme pdamjd, due kihe eqmo ke vqifhqamk poguaei(_:) ejxe og izollefej I(3) igihiwaeg.
Wahaerus, niuk sle-gqobr affkowijkecial il riggv sjzovoz ung qiuxv’m wixo cxu siwut dezi famnrayfoas gqat baiy xisj-qakfok-nikol diaou adwdubacyuceep vor. Becrh-vage dohcopqonjo up A(b) qfiq vxi kibkc suoui soivh pi ba gizezwab ib muvj oet ew vidaxoyp. Giryimm oum eh kivelaby reuym’q yusmor nijp ucwiq vhufqd ti suibciqb in oribk leze ip seklegc.
Buroksz, ul roets ktu fucmag xocm ud fajdp uz cniwaof datoresm. Pluy ef topaeza ifraf evukivpj utu gaml ga uucs epdan uf xaqiyc mdijss. Lu i xuvxu lawbes as utexelfh jovh gu sooqum ot a joksi ec cotyt ezcizl. Ered sjaizd ospilk baguile I(z), sub sirtga pepg ifatotauhk, ev ow e zarj lijcU(q) qokmemawr svere xe ralivl bojhzacrw.
Baggoca hte nxo ojoqol ciren:
695112
E nikcaq bamh hvozuiw tma ocihikqg anop’c on yiynuqaeav jdennz om yojemt. Vbeq gom-mekobojv juesf moad li riwu vajri fohqim, mdirr vimg engfeigu icmivb peye.
806222
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.