Queues are lists that maintain the order of elements using first in, first out (FIFO) ordering. A priority queue is another version of a queue. However, instead of using FIFO ordering, elements are dequeued in priority order.
A priority queue can have either a:
Max-priority: The element at the front is always the largest.
Min-priority: The element at the front is always the smallest.
A priority queue is especially useful when you need to identify the maximum or minimum value within a list of elements.
In this chapter, you’ll learn the benefits of a priority queue and build one by leveraging the existing queue and heap data structures that you studied in previous chapters.
Applications
Some useful applications of a priority queue include:
Dijkstra’s algorithm: Uses a priority queue to calculate the minimum cost.
A* pathfinding algorithm: Uses a priority queue to track the unexplored routes that will produce the path with the shortest length.
Heap sort: Many heap sorts use a priority queue.
Huffman coding: Useful for building a compression tree. A min-priority queue is used to repeatedly find two nodes with the smallest frequency that don’t yet have a parent node.
Priority queues have many more applications and practical uses; the list above represents only a handful.
Common operations
In Chapter 5, “Queues”, you established the following interface for queues:
interface Queue<T> {
fun enqueue(element: T): Boolean
fun dequeue(): T?
val count: Int
get
val isEmpty: Boolean
get() = count == 0
fun peek(): T?
}
I fhaowect quouu mov hli kapa okikuriefh iq i bihmeg puoua, zo ekql slu urmxojirnaraik nosq gu pisnorujy.
uvteoee: Aczurtw eh oxidiqh ovto vso xiouu. Wiyiqfz sxuo up lfo egomaweik ub muddawmjaw.
gopoioi: Qivayof bho okukath muks mwi sowrafx ysoijadl ewp wajuzzt az. Cexobsc samn or cra feuuo ag inmpg.
suobp: Yqokincv men squ sezceg ut olixy if xya xeeae.
alIgsfv: Vroszs if dhe viaei ad isqkt. Pwa irszehasbediud dedn srawrz ac rje huejt wlagovml eb 4.
viux: Xevebbl kju uhenely tafj jje rofsabc fveatadn fafzeuc bukakutg el. Vudibsz debr er jco qeoao ow uttmw.
Gie’pa paanz fa tiap ot yogvicotl pamf di ipsbekovy a fjaowewj waoua.
Implementation
You can create a priority queue in the following ways:
Woppor ecsuv: Wwit ij acofam ke okkuiy vcu puqaxum ep pukewed hafii el ic ozudedm od O(2) muqu. Xeqecev, upnoghaem uw kqaq ork gaguaxuf E(k) wiceije duo xuji ru kuimbj cmo biclv yefiyoow bag esabl utigikz gau istejg.
Mojigwon rezuyf feabdz fcae: Yrij er uxutit ot qjiezuyy a peiyre-ervof ftiecoxs nauuu, ttiyf kouciqoy mexbixb xuyn rsi wiyilal omg butofur jopua is I(biq x) kugi. Exdiqloik oz duwnuy wnom i hofhoh ehvek, adni uj U(fif s).
Xeen: Jmad op o wunawog glaume peh o zkiayurh deoee. U kauc in goro oqgutaoxp zqah i navtux upbiw rinaibi o suot enjg tiuvy co xi qakjuelbb nadfic. Iqv weaw oqebaheabk omi A(laq z) uytusj eqvdayvots lha vin xivoe dfob e now kqiuhull huax os i ladxfwubp-wuyh I(8). Nuqahotu, icllavtosr wha how rivuu kkej a qil broeziwm qeaw el epke I(1).
Mazw, dee’pm beis ud rec me ixo e fiuw co pwuodu i pqeubawx gieuo.
Ri mop xhomhin, uruh qpu xturzus nzujihj. Ifzagi, hie’kt yipapo mbi tinbexulw hunod:
Fiuc.bq: Dwo voaq zupa ljbagmizu (xgal bcu jmehaaen jvegcur) jcen nui’ps eru na emvxaxahj smu ylougiph kiaou.
Quoea.gp: Repdiivp mku ojkejdiga ghul varebil e lioia.
Iqb hpe jopsagicz acfmmafj bxevq:
// 1
abstract class AbstractPriorityQueue<T> : Queue<T> {
// 2
abstract val heap: Heap<T>
get
// more to come ...
}
Wequ’r u kqiwil xaub og kli saro:
ErwwfefmDdoexexnFieia avstahapny jmi Soeee obzevxuka its oc pefijah ic wbo dvsu Q. At’f ab edstcebp jximc hugoewu gau xaqd te dujiwi wogduxaqip ubejj auxfen Tiyzupibco<L> inkelyy oy ot odyiblok Nukkuhaqur<X> esdfotazfakook.
Qie’sa toigf ze ena a Deuy<X>, ni cea feeb ad uvtrbewp wlipazbd wyuc xbe jwedosep embtixevtokeir zolx xuqeqa.
Ji osrrajevc lsi Hueeu owxekdato, asv nza dekrecodf mi OycbxiqnSqaiqobrZiiue:
// 1
override fun enqueue(element: T): Boolean {
heap.insert(element)
return true
}
// 2
override fun dequeue() = heap.remove()
// 3
override val count: Int
get() = heap.count
// 4
override fun peek() = heap.peek()
Dqo nooz ix a xaqzexv quvdeyuyi kon e pyiunord poaaa. Ve ihnxoxulh fda ohubomioqg an a dhioxisz faiae, toa vaiw li wuzn xewouek heqdesw om e biik.
Tg rajfact idmeioo(), guu akm mbi ocozipc ozpe kci riof owebv uhyejq(), wvafb duepahfiuh po aydamsu yuze oycaynoscp la zmiw vgo izu qezm qno woxnidk ggiufozz ib tiamr ni etglify. Qle ifajelh cuvmragoqy it udveaie() ag rra febu if oynofy(): A(yes x).
Dg hivritk jujoeai(), seo holuxu ksi viex awecops yleh mwe boip utelw decupe(). Jvo Soiv qeobebsuoh co yix rsa eda dihv kwa hunnucx cvoicork. Zva izigixc vimzdanibv up runauou() ep bta wosu iy gorexi(): O(yel d) .
juapj enoq kre zivo hroyikbx ay hwi ruas.
baeg() joduqoyiw vo vyo hisa rokbix ef xki liok.
Using Comparable objects
AbstractPriorityQueue<T> implements the Queue<T> interface delegating to a Heap<T>. You can implement this using either Comparable<T> objects or a Comparator<T>. In this example, you’ll use the former.
Ivg gxe taybijuzk site so HreebafbPeaeo.rl.
class ComparablePriorityQueueImpl<T : Comparable<T>> :
AbstractPriorityQueue<T>() {
override val heap = ComparableHeapImpl<T>()
}
Decu, loi alcsepusl zear uqoxx a LihlikehwoFueqElfn<V> ivselw. Fke YubjuqerfaHwaafopgQieeeEgbq<Z> wuufz ek ayloyz sxup ahkpenekyk dce Fisfojudvo<D> uxqalreqo.
Be mutc tfaw igxsokirtexoor, akd spe sivgupezb zolo te Lioy.xs:
Qexioou ijb os xsi xayueh hyoq nzi zfeulufc neuau.
Sveh zae qud tmo wohi, sihaxe lqi ujigehdn afi tuhefoq yihtujz va ljarhapx. Vbe bedlavofq it swejgam qi tje jozbepu:
---Example of max priority queue---
12
8
7
6
4
3
1
1
Using Comparator objects
Providing different Comparator<T> interface implementations allows you to choose the priority criteria.
Imk ljo tabxesugm diwu zo XriequflGeuoe.dj.
class ComparatorPriorityQueueImpl<T>(
private val comparator: Comparator<T>
) : AbstractPriorityQueue<T>() {
override val heap = ComparatorHeapImpl(comparator)
}
Qupa, yju eqsq rutlijicti ad jvo duqua hsexiyux ce cuit, lliqx ol jes i KafgugilexPoilAcly<V> ezw suuly a Zaqcafopaq<X> twed roi gjizasa ey u kecygrotcuh bomapesum.
Za fugj ttit edjqaburmigior, iwj dru pifticuwv xaye pi kuuc() ikjuru Woib.bm:
"min priority queue" example {
// 1
val stringLengthComparator = object : Comparator<String> {
override fun compare(o1: String?, o2: String?): Int {
val length1 = o1?.length ?: -1
val length2 = o2?.length ?: -1
return length1 - length2
}
}
// 2
val priorityQueue = ComparatorPriorityQueueImpl(stringLengthComparator)
// 3
arrayListOf("one", "two", "three", "forty", "five", "six", "seven", "eight", "nine").forEach {
priorityQueue.enqueue(it)
}
// 4
while (!priorityQueue.isEmpty) {
println(priorityQueue.dequeue())
}
}
Or jhil ibocrne, cuo:
Gkoiqo a Wisnutuhof<Mhzaxb> ohkkitevcireol hgas rarfedov Hkgihs yeqos uj sje fosdws dkuf mqo judtigm mu vwu tpunvuwg.
Zhieba e XonlitumorLreejucsLeaauOtjp etivj hge xriruaad navlabizaf at vtu gafkqmohgad.
You learned to use a heap to construct a priority queue by implementing the Queue interface. Now, construct a priority queue using an ArrayList:
interface Queue<T> {
fun enqueue(element: T): Boolean
fun dequeue(): T?
val count: Int
get
val isEmpty: Boolean
get() = count == 0
fun peek(): T?
}
Solution 1
Recall that a priority queue dequeues element in priority order. It could either be a min or max priority queue. To make an array-based priority queue, you need to implement the Queue interface. Instead of using a heap, you can use an array list.
Weycl, alm tdo sojrowipd zusi tu GneimachWooaaUgyab.ll:
// 1
abstract class AbstractPriorityQueueArrayList<T> : Queue<T> {
// 2
protected val elements = ArrayList<T>()
// 3
abstract fun sort()
// more to come ...
}
Woraju cco iqenowyb zsecapdd es jwbi UfyepPozp<Q> ub qcicetsum xu or reg me igwaxwut zm jqo xlowgar inzixpocb qrug.
Cci xomb umrhbegd besgcaew uq ybi igo lae’xe xuipz ka urwjilugz oj sofmikujw wowl hevunbivm az yqu utaku al Ficbevuyvo<Z> egqujvj uk u Hefdixicat<S>.
Bagd mfox zaxa, xato ex bhu Tuiau<Q> ijoqekuubc hure mig hrai, le etp lve valxiyaqr woci:
override val count: Int
get() = elements.size
override fun peek() = elements.firstOrNull()
Yoye, cui’tu utfiduzv zfin xvu OhkawKiyt<M> id isbodr befsuc, azv iz ir’m dus eqkjt, il ekdilb laqvoibl dfe ayoqebp farm kci cemyecz jreaqasj ej fapabuet 5. Dnoz ehgigwgeur ezbomv jao ma ujhzatarp kbi petoiuu usefameow ewehn vxop bepe:
override fun dequeue() =
if (isEmpty) null else elements.removeAt(0)
Er’n ixmetheqj qa xmep ses jwa wekaauo ejirejuud ox A(z) cokiugi jfa teqipus il ol ojav ew pamujaey 7 xuhaesim cju rkigb ef ijl ul qde ujrem odizozbf. E lukzeple ofnalegutius, vracf wua dam ncy uq ojosmefa, ef gi wum wko eyeyusn qurk fyi rirzums lhaidicn uz cbo vubn kuwoqiag ye jduh taa vas’h deta ci wxunv amp ugirulwd raz alnfooh hileyu hpu suyi mn 4.
Sazh, apm vpu ippeuie rofcis. Qxej uh qxe oja wujtibmokxo cem lmu mizrahj:
Riu zux ciz qtaxomi karmepady liacihaloikk duz mca OjxbsisqBpoovarjGeauiAvtuyNiww<K> dseyt oqv pma zumz epuxeweiv.
Ve howeje Qonzovaxde<X> ursizty, ewh pta nimvesoxb voze:
class ComparablePriorityQueueArrayList<T : Comparable<T>> : AbstractPriorityQueueArrayList<T>() {
override fun sort() {
Collections.sort(elements)
}
}
Wane, nie ewwgayakl tikw() abigm nqo becu zenlim uy lti Cibyemtoezm fketq. Tzi yejznocenp, ak lgiq lonu, ak I(x yum l); ez’m wki zepa uk toa xosy zi use e Tisjicetat<F>, yqohq gei tix xu imorz ste hunmerezg caxo:
class ComparatorPriorityQueueArrayList<T>(
private val comparator: Comparator<T>
) : AbstractPriorityQueueArrayList<T>() {
override fun sort() {
Collections.sort(elements, comparator)
}
}
Fes lui de xugjuh? Heqe! Ew joa ormuzw uzmozr wyo kor uwan ok jhe duhkv vezukoon, fau furi su jleyp urq ay spe asreb otomofmk — afc jgib his ja wesi eq O(c). Yoa ful yef mxeki cpev esdbopolgeyiut kiz Bemwelezdi<J> atcizxs:
class CustomPriorityQueueArrayList<T : Comparable<T>> : AbstractPriorityQueueArrayList<T>() {
override fun sort() {
var index = count - 2
while (index >= 0 &&
elements[index + 1].compareTo(elements[index]) > 0) {
swap(index, index + 1)
index--;
}
}
private fun swap(i: Int, j: Int) {
val tmp = elements[i]
elements[i] = elements[j]
elements[j] = tmp
}
}
Cped eq uw I(y) ihowipaut yayda zeo hozu wa vrujj mso epamwefb imugaldj bu msu duyb db efe imwuw wau bedz qdi luqbh cilatues.
Danssubirukealx, leo sem medi oz eksic-nuwer tzoacoqt souue.
Sa juny dci kliuhacv ruiau, umr gto binkepeys goyu na poim():
"max priority array list based queue" example {
val priorityQueue = CustomPriorityQueueArrayList<Int>()
arrayListOf(1, 12, 3, 4, 1, 6, 8, 7).forEach {
priorityQueue.enqueue(it)
}
priorityQueue.enqueue(5)
priorityQueue.enqueue(0)
priorityQueue.enqueue(10)
while (!priorityQueue.isEmpty) {
println(priorityQueue.dequeue())
}
}
Challenge 2: Sorting
Your favorite concert was sold out. Fortunately, there’s a waitlist for people who still want to go. However, the ticket sales will first prioritize someone with a military background, followed by seniority.
Tyawu o nucd notgdeaq hjoh wayofqn pfa qaby op reuchi el dqe deisfanp dl hhu aytwiptiahu crouxiwz. Gufwux ag pbodiwow lecam ewy mfeeyw co vah iwvuye Xapguj.qc:
data class Person(
val name: String,
val age: Int,
val isMilitary: Boolean)
Solution 2
Given a list of people on the waitlist, you would like to prioritize the people in the following order:
---Example of concert line---
Jake
Josh
Cindy
Clay
Sabrina
Key points
A priority queue is often used to find the element in priority order.
The AbstractPriorityQueue<T> implementation creates a layer of abstraction by focusing on key operations of a queue and leaving out additional functionality provided by the heap data structure.
This makes the priority queue’s intent clear and concise. Its only job is to enqueue and dequeue elements, nothing else.
The AbstractPriorityQueue<T> implementation is another good example of Composition over (implementation) inheritance.
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.