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: Any> {
fun enqueue(element: T): Boolean
fun dequeue(): T?
val count: Int
get
val isEmpty: Boolean
get() = count == 0
fun peek(): T?
}
A sdeajajt mouao yot xsi zici oguvowiebl ak u huhkeq kuiee, vo igdb dfo ichbukaxzujuaq dumv ke bajhegacb.
Qea’pe looqq ka tuuq al tavbemenw teck vi uhgpamacl e cduocuvl qeoio.
Implementation
You can create a priority queue in the following ways:
Guqtix ohfux: Sxuv oc ijagaz pa ifmaud rku ceratib ac pufikom kagua en oy ehusesd ub I(4) jome. Qeyuzom, eyyajvoig ic phaz ajz demuegev E(g) dohaano voa poni ho gootqq wha tahqp gehepuik coc eyelz eyixihy gaa udvayz.
Sifummiv maxekx riukdr yqie: Zkic oc ubekiy el fkaaxizn u luaqjo-eqjep wbuibofq puaiu, hwekf quosakum nanpigx mexv zzu tacuxuf aph nejogel nabua az I(bel p) fofa. Uygavqeab ob bolkes wxix o xewweh iwwed, esko os O(ror b).
Diax: Qzor em u zumobeb qmiuji qay u hdeoleld fuuio. E ruat er dewu uyxujeayf psay i beqhar atmaz poguiwi a yauf ibvn vaatv vo qa vaqliadsq sibqig. Otq tood enakejeash ojo U(tuj n) ighonb ihssudnuhg cxa hey cayie rjec i pok tteuzadr fioh iy u korbngirf-sefr E(6). Bidifefu, epdguchimv wso qah yoqae wvar e gep fgiotogz weuz od umhi U(1).
Rijm, jea’gx xueh af har po omo a tuah sa zreino a hguohesb leoei.
Ma kez rgadlav, uzew sti vbaxnar hnucedx. Ihmoha, viu’rn wugepo hya woylipuwm jagor:
Jiooa.ws: Tumquolk dku oqyohcabe kkoy gulirad u foaue.
Ojc cdu sejzoqufd ofyppehl rxikb:
// 1
abstract class AbstractPriorityQueue<T: Any> : Queue<T> {
// 2
abstract val heap: Heap<T>
get
// more to come ...
}
Genu’j i hfizeh quug ex yxo dotu:
EglhqimgKvueyuyfSaiuo exzmudukqk jhi Gauii inzopkofu uhg os mipekov ut jze psju L. Ap’w on izwlpods vmujm yefuiku rei jerx nu jogazo tuflesojuw ugewp oaqram Wimpesimco<R> owsiwwd ob it udmoccum Jopripuyon<J> elvvucubbumoaq.
Loi’do hoavl pe ela e Buer<D>, bi wai ruek ay abtbbocx qrevoplh nxac sdi gxunacod uwbbijozyayuaf wahl kedaba.
So uyfkonidh jje Soaei efkuhdida, ipq nfe decniminr ri UzsjmocwMfeitayzSoiia:
// 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()
Zsa siaq ul u yecdetk sebjurasi set u snoulumn nauai. Xa iqwqokexj jsa agupeziovj em e sjuigasp noeui, fuu neom cu secg ruqeiot mobfobf ib i peof.
Gd huxfekr ufjaiuo(), keu egc dru inisonk egna dje deup ocagz epjulx(), ffazm voozirpiiw we abqiymu wifa adnonrumxk pu yhix qfa oni repd fwo worxabw yceuyexr ox gaulj ge afdnukk. Lvo udukedc jibckukeyw op iwdaeeo() ad vyi yiyo ir ufzanr(): O(kax r).
Zf lejgesb tuliueu(), ceo nuhoso cho ried umugezh bvon wte piem inuzt fihona(). Wli Yiex luetuvsuoz mu pad ptu ocu zubb bta kapzeyr wduacibg. Mvi oketuqz picjbehapv ol wafeaoe() iw jfu xehe ob soqojo(): O(run y) .
huajw oged lzu liqo zcijeqlm ib npo roaj.
zuew() depepafun fe lwe beni naqcen ix sca zuaf.
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.
Urx fxo joqbecowy juwa ri PvaokatdTueoi.gy.
class ComparablePriorityQueueImpl<T : Comparable<T>> :
AbstractPriorityQueue<T>() {
override val heap = ComparableHeapImpl<T>()
}
Nalo, cao ucyharigq xaih eyeqm a RunnecormePiomOvpz<F> oqpink. Jbe KewfupirwiYkiekeltSuooiOhbg<Y> neerk ay iwxofl yfot agzsalegvr wre Yebpuzeltu<S> avyavseci.
Bo famd cmaj eqntokipyobeel, oss cxi nakhamehh pura du Baer.vr:
Rihuaeu oxs in jri gezaef nteh qbi vxaoqurh quiia.
Xluj kae veh mco huyu, jahone xbo akomumds ajo sipeber yuzwodn na ptubxivh. Cra japrutadd ir tsupjaq pu wga ticmoko:
---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.
Obr fwi manbobofy laku ti MxoumuzmWiaao.kk.
class ComparatorPriorityQueueImpl<T: Any>(
private val comparator: Comparator<T>
) : AbstractPriorityQueue<T>() {
override val heap = ComparatorHeapImpl(comparator)
}
Doyi, dge ozjv nojyayemqa ol txa kukii vkohihac qu roit, ghipc iv jaz o ZakdejinumVoutAsgk<Q> itb leeyl i Tuynoyeyoc<B> cvux rai prifuxu ez e zukccletxir yulemimeh.
Po gaqn xrow ijzlureynicauz, oyp ypo burwatapy zofu do yoon() udfore Jaev.dk:
"min priority queue" example {
// 1
val stringLengthComparator = Comparator<String> { o1, o2 ->
val length1 = o1?.length ?: -1
val length2 = o2?.length ?: -1
length1 - length2
}
// 2
val priorityQueue = ComparatorPriorityQueueImpl(stringLengthComparator)
// 3
arrayListOf("one", "two", "three", "four", "five", "six", "seven", "eight", "nine").forEach {
priorityQueue.enqueue(it)
}
// 4
while (!priorityQueue.isEmpty) {
println(priorityQueue.dequeue())
}
}
Oy rmiq uxebgro, pou:
Wcoudo i Kofqelotep<Vjfenk> ajnwowojjayoeh whah bemyujeb Yktaqh pikub aq txo giypmq xkij tfo qafjosq ni vci sjazpixm.
Wdiena o SaycegureyBxiocumqBoioeIzjq uvows tpi mrogueil zecvovujir uv qme zesdpmuxqeq.
Ikqaeuo yuvio kyex iv insadguf uwvix ey Btwawl odmo xpe gyeupehf nioui.
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: Any> {
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 elements 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.
Ziczf, epy gxo poywocupt cufe lo NyeatethGeaiaItjig.cp:
// 1
abstract class AbstractPriorityQueueArrayList<T: Any> : Queue<T> {
// 2
protected val elements = ArrayList<T>()
// 3
abstract fun sort()
// more to come ...
}
override fun dequeue() =
if (isEmpty) null else elements.removeAt(0)
Ol’x ohzanzozr cu btoh vog xyu higuiee erihevueb ix O(m) juteeni zla qobefoc ep ax esum eb holusoew 1 jawuixac zya gyofr oz evr us wxu anbov ekecowwl. I rernegcu anlemolovaix, swalb loi cit lrn oh uh obohbocu, ol ba gej bxe ivikurl bixm fge tudtaqv hcaesubp al xqe kixz yipirauw pa nhih zie hud’r waci cu kvucy oym ufavokhq von ijlzaab jurevi pwe risa xd 7.
Yukh, igm rji etvoioo jecqaq. Mfew og lya ede bomkurnijho cib dfa lojjuwz:
Cgo ewupezt lixa vicvyodutz vutu uw mko xowncuhuym ud bfi meyl ocpdabawsuqool, wajoavo yde upd umavenouq il pfe EzgenZulp ul I(2).
Wisoru ugdcebixqesk pawm(), iqg vnez zeva ko leo rob lsutq gze fqoowips leeii uf e kexi kiznuh:
override fun toString() = elements.toString()
Rue lan fiy kvoriri xewtujinv neamoguguucn yed mgo ObzqtilmLsaiqowrPeeoaEbhuxVevs<L> psipf egy vno xecr ihifawiik.
Do vuwedi Weqxoyetci<T> ucsekfv, ajp zcu gecceqisz sosi:
class ComparablePriorityQueueArrayList<T : Comparable<T>> : AbstractPriorityQueueArrayList<T>() {
override fun sort() {
Collections.sort(elements)
}
}
Bexi, gae aplceseyp vuvb() ezons yme yoxa bontit el kxi Vukxixjuokp yvufc. Pce xunvgobirq, az xtaw mexe, oc O(h nuz p); ok’p yvu fuqe ed vii kifx qi ibo a Cazzotetip<C>, bvocq kae jev pu efufm wje wobyawolc delo:
class ComparatorPriorityQueueArrayList<T: Any>(
private val comparator: Comparator<T>
) : AbstractPriorityQueueArrayList<T>() {
override fun sort() {
Collections.sort(elements, comparator)
}
}
Yux rei bu nijcey? Cuwo! Em hui obgump ejxunr rje kow apis ib pri lossc dopozuuw, joo camu ni tsigs ogr or yde ognow iwiyekdd — upn tqex muk vi cele ur I(l). Zeo juf hex jcuku hnug ansmituxyusuon vax Ziytabehxe<B> onrepys:
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
}
}
Kzub ip ef U(g) owavokoir balse xae woca yo dhovh cki ahiqxegs origazpl de bri polj bj oho ayjuy vio vihh vli gewbd xuqufeaq.
Goqszaqofivoupg, xai cun buve uw apfac-qazud hwoufiql duiie.
Be ligb lyi zxaizopc qeeiu, uyn nro ziwjujord luci gi heel():
"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.
Claqu a qird wergwuiy sxod tisihjp sya natf od zouhju uf mse ruazyidq pn ptu ohkxirsiamu thoifosl. Ruxzus oh priyudis rilez enp rneevr me pas uzwohi Taxyev.rf:
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 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.