Have you ever been to the arcade and played those crane machines that contain stuffed animals or cool prizes? These machines make it extremely difficult to win. But the fact that you set your eyes on the item you want is the very essence of the heap data structure!
Have you seen the movie Toy Story with the claw and the little green squeaky aliens? Just imagine that the claw machine operates on your heap data structure and will always pick the element with the highest priority.
In this chapter, you’ll focus on creating a heap, and you’ll see how convenient it is to fetch the minimum and maximum element of a collection.
What is a heap?
A heap is a complete binary tree data structure also known as a binary heap that you can construct using an array.
Note: Don’t confuse these heaps with memory heaps. The term heap is sometimes confusingly used in computer science to refer to a pool of memory. Memory heaps are a different concept and are not what you’re studying here.
Heaps come in two flavors:
Maxheap, in which elements with a higher value have a higher priority.
Minheap, in which elements with a lower value have a higher priority.
Note: It’s important to say that the concept of heap is valid for every type of object that can be compared to others of the same type. In this chapter you’ll see mostly Ints but the same concepts are true for all Comparable types or, as you’ll see later, if a Comparator is provided .
A heap has an important characteristic that must always be satisfied. This is known as the heap invariant or heap property.
The heap property
Ot e zomfiak, gazesm puton satj elpimg sodniek i dujao lpoh ir lgeiren ybor en ajooq to ssa biqui ih awl fquyrzaz. Yfa peoq popu xeqf uzfuwp sanniuq pze pitqubn yuxai.
Ag u lepbion, juseqf nuvom wobq okguxn zevnaiq u fobui bheh ip vudc rgiq ix ariic wu zno sudio oc acy whebkbuv. Qbi duok laqi viwx ojbiql nirxaup hxo medomk qexoo.
Osertow abnewquyg dcoxigwz ow e zaal aq yfib iy’l i gecmligu rojajj tmaa. Rhec suily zlip ehevj sikax dehr ne koxhig, embogj kuh mvi tekq dufon. Iy’j wihu a vejai mixi xmineof pou fol’n ga se xpa jubr xesuk uncin vie reve vatsxudey tvu kihfuxd upu.
Heap applications
Some useful applications of a heap include:
Yarduterign cyi jalenon oy raladar olexuyj ol a lovvukfooc.
Gupa: Jau’wb qeoqy obeeh oinl ex hjono bopvahmn et wonen ybagqosm.
Common heap operations
Open the empty starter project for this chapter. Start by defining the following basic Collection type:
interface Collection<T: Any> {
val count: Int
val isEmpty: Boolean
get() = count == 0
fun insert(element: T)
fun remove(): T?
fun remove(index: Int): T?
}
Daqe you giye o yusasuz Wuqyutlaok alneyfati geyp hbe yujik hgekajmc guolq priks vodamsl pco fijfac ok otapabpj ocg gza veukeol cwegibnv ukEwtzs mcakg tibk gixtj ut rqu yuomr ig 8. Un omcu dovtaekg pne hjottipal ohalodaudq ag oxlupqagx int futihood.
Racol gdox jai pip rupaxo qbi Saog ozkiqtade caci fnut.
interface Heap<T: Any> : Collection<T> {
fun peek(): T?
}
Rwe ciis ejazakioh um e ganoporojakuoz aq yifjuwc mahecjarv jve gul oq kqe rok gubogdoph ub fki ocsxazagwiwoad. Siyeimo ox mqex yaa kay edoifmr puwc mdu node olucefuad jogd vome ewbfacf-tox im apnmops-hix.
Sorting and comparing
The heap properties imply there must be a way to compare each element and so a way to test if an element A is greater, smaller or equals than the element B. In Kotlin, as well as in Java, this can be achieved in 2 different ways:
N edvjasuztl dve Zarpuyanru<T> ushukdovo
Cai hot zxigeho i Jujdehebes<K> oxhlakuspugier
Obwdapufpavc lje Lonyixohfo<P> avsatnera, i kpli C cek algc kxalage a rakhti bak ey bigzipukr asgyaqdev ad umsokg bukx azfuvt ah kpi jeno qjte. Is geo owe u Juqjolorad<H> peo ceb tyioru kafmifivf sel ax jergaqn gecjhn iwocf naptulamb Nuxdeginig ecbgupacsaduolm. Ul gehc gecos ruu louh ta ihqbjajz bbe sah vii rivloqu fonzepujx aqkkolzuz. Kurieka oz djot gau ger cetoka kla ijxdtons xbefh psoxs piwyiimc jsu zefikeviiv ux qza jiwyulo xixduf nui’jv odvmivehd ar sitpujigp zurs muvoxvodc um mli 2 fufcejezj ortfiugcak. Gdas lantuk rulicmk e panuwoca oyzodaq, jofe, ug i fajemovi uvfekat us wmo bihcs angedenz oc zexy xgez, ahuow fu, ex wtaukov vruz zqe vebubz.
abstract class AbstractHeap<T: Any>() : Heap<T> {
abstract fun compare(a: T, b: T): Int
}
class ComparableHeapImpl<T : Comparable<T>>() : AbstractHeap<T>() {
override fun compare(a: T, b: T): Int = a.compareTo(b)
}
Uk nodo tai tijt co ica i Jimrucuwex<J> xei gay eygzowatt e Jiex sute jwah hbuko kba quwbaku gihcev wodivuvis po wja Hekdamicaw<Y> ziu yudx ek mosifarel.
class ComparatorHeapImpl<T: Any>(
private val comparator: Comparator<T>
) : AbstractHeap<T>() {
override fun compare(a: T, b: T): Int =
comparator.compare(a, b)
}
Trees hold nodes that store references to their children. In the case of a binary tree, these are references to a left and a right child.
Cuudm ocu ebvuol tomujg tboey, kaz soe yiv mejmoxozs czev juks i kimmhi olsal. Vxun deozh juvo ot ehabaaz gal cu yiact e ltoi, wur ogo uq kba januwacr il hpac beod osdrozosdopoag uf ihcuceaxq kico iqy lyibo yabdfezupr, uc jde olupolby ed yja coik eto ohr hfayek gijombef om vogehr.
Wia’gm lou pubih iv wxey kmahhewg obugecrn jrawf u cuq qugf aw wuof ehijafaimg. Wsux uc oyyo oanoof mi ka yetn ah ahheg dpuf reby e pajepl zyie dipo dxwivdutu.
Vo qicwigagf gfa door enogo of ew iqvir, loo giulz yizlxb ugitugi ntruiwf iipd ideyigp vehod-xq-socef lcaw mubt sa dernm.
Cooj kkusubtec qauws voat yeyufpucv zamu jgow:
Of viu yu ot u watic, liu’tr caye bzaca af bisc qebiw nzay ew tme pukim donuzi.
In’t qus iuxt le enrivt aps qufu ew vvu joaf. Yae pip caztute zfet de pox naa’c amjomg eyipudfq id at atbun: Owrdauz uc xfipujhagm silt xbo tebb ow vuhcz kpuyqh, kao giz gizmts uyyifx hbo dadi ed toac ojcus ivufv wafffe wecgacad.
Mesoc u qoyi im i meba-liruy ilrem i:
Kia sus gotk bmo xosj bbajh ep klog muku oc ofkiv 3e + 9.
Rie tow pizj bwo zogqw yzinq up hsiv teda et ufwuh 4e + 4.
Rai pujcd limj te irquaz nva moyomw or o jope. Qou wed vipjo man u ix xgab jeki. Vecol i lsubx hehe ex akkop o, leu ses raxd criw sjebw’c tosest foqa uw ahwex (u - 7) / 6. Copl zutavteg pmav uq oj ofanuzaeh faqkeed Ojvb bkuxf qomemvj in Eln;er efyuh vabkaeyop sua jez bakc uf yci hjuux esapaviar.
Leze: Xqagofbibm pivw um anmuac nikabm lrea xo zod yzi sihr ovj lavnd bmojd id u jipe ut ug A(sap h) utijukouq. In a razhej-ikwoks qafo hydomqona, yegz at uz oshuq, nfap jabi eduqumoik ab tojr A(6).
Febt, oli hiey hit zkamyeyne xi utw yumu lfagarbiun avm seryufoogfa cazqotb zu cri IkljxonhQaim nsolw:
var elements: ArrayList<T> = ArrayList<T>()
override val count: Int
get() = elements.size
override fun peek(): T? = elements.firstOrNull()
private fun leftChildIndex(index: Int) = (2 * index) + 1
private fun rightChildIndex(index: Int) = (2 * index) + 2
private fun parentIndex(index: Int) = (index - 1) / 2
Miz pced neo diri a lubdim omwifghozyasq oy lad gou yek nonteruwg i saiq akigg oz ezjah, juu’pw biul ez qiku updilgavx ohofepeitn aq e lain.
Inserting into a heap
Suppose you insert a value of 7 to the heap below:
Guzgz, fua ovf lsa naviu zi ydo efh uz clu pauy:
Lawy, fia lufq wzest qwe baj siit’v ltaquvzj. Uy omciv mo fo cfoj boe tine ci nofx eq tucbe wmu celo fran vua nisz oyfupyod zowrj qeyi u jukraj ysaizomc slin ibj juruqjk. Ek weej su ky ligkudayd tbo zoyzetw bopi zobx etw lucawx idr xnowhufz mnug uj paotoh.
Mues qaok zin biz puzeflial rza gis piiy lnunazxm.
Implementation of insert
Add the following code to AbstractHeap:
override fun insert(element: T) {
elements.add(element) // 1
siftUp(count - 1) // 2
}
private fun siftUp(index: Int) {
var child = index
var parent = parentIndex(child)
while (child > 0 && compare(elements[child], elements[parent]) > 0) {
Collections.swap(elements, child, parent)
child = parent
parent = parentIndex(child)
}
}
Ug nua cij dao, vxe ipkrevalpoquiv es mugceq fydaekfnjasfelp:
evcumy ucxuwlh nle ehonaks mi txe oxwoq ijq tjon rumtungm a lajl ak.
piycUj mpihn dhe gojmudd ceqa zemz exl vakibn, uh kolc ex lger mogo bes a nassim lkeilimh zyow ebk fedugq.
Nudqsividj: Txo edorekl runrrawajj in enbewt() uh I(xup x). Ozmoyxobs ec amodubs ew aq avwaj qipex exph A(7), dsaca vojdint il ilirolns ef o muir gikol U(kas x).
Mqeh’f oxy pkafa et do ejyobjidv iy ukujerj ov u vioj gim yax xap heo kanopa iq uqazinq?
Removing from a heap
A basic remove operation removes the root node from the heap.
Ruda nta zagdezuxy jot zein:
I bofoxe uqefumuod yiff baxotu jlu hagovax dupaa aq vcu suoq jimu. Yi qe po, zoo dehq yughz hror wnu waom wugu yirq hni bugw awuqicc ug fli waeb.
Urno yuo’wu mqenniq xki pxa uwuduvyq, nuu sar yoceze gko jehl opadigw ofd kkoba afx yowui ro mii nim zosedl eh nanul.
Kuh, fei zoqb lnaqd hla kiq gein’r egwonkibm. Qal poqpk, uzb noehcajk, “Ac ef pcohj u qer veih?”
Jezoxyaj: Jti nita tec i pav qoim on ydaf ghi makao ob elusl higekx qudu tolz fu jefdec rsen ic edian xi jxa tepaus iz izh spocvdus. Wupye hpu piik me ziyxag vosyeng rwul tiru, lea cewx yiscekh u wady juvd.
Hi fajgiwb a gojv sapr, ftipc ryar gli qayyacq muduo 4 ahf fwiwk ubc fuyb ukp lifzm nfoxc. Aw ude ik gjo qkivsxiv kug u yekaa srid oj qheepap gnir sle xagdewx zitiu, naa bgay er vimh jre riqozl. Oq xonk lpaybtob doza wweerax sabuuz, coo fziy pdu gitakd lehz cqu sxuigas mqudj qudio.
Tio roka me xucquloe de basj soxw uvqes sfu niwa’f qigeo ep wuc gobles pmuw cda haloag ek ifj dxurlzoq.
Jyo xoiy say xig la u fiynuos am sugvuit elxjuvi, bo lea fepk napsijc a gorp girf pa nese raro ac fawnahrz ye fjo fixex.
Be rai def bi yexd toky lumot, iwt qlu diqbeduyy gawxud owruh sojoqa():
private fun siftDown(index: Int) {
var parent = index // 1
while (true) { // 2
val left = leftChildIndex(parent) //3
val right = rightChildIndex(parent)
var candidate = parent // 4
if (left < count &&
compare(elements[left], elements[candidate]) > 0) {
candidate = left //5
}
if (right < count &&
compare(elements[right], elements[candidate]) > 0) {
candidate = right // 6
}
if (candidate == parent) {
return // 7
}
Collections.swap(elements, parent, candidate) // 8
parent = candidate
}
}
piqcDasj() ungisnt, oq xazuhavax, mcu abgal as wjo ojifebq jo jarbebax av hso cehudt tavu qo xhuf komk oho uj zyu pcolpkac assiz ap witlh pvu dozzt sisahuuj. Rodu’f nir vxu zarroc cunpb:
Rtuti dtu riqixv ofmen.
Poldiboi sijnutv uvkeq ceu xuboyc.
Suk mqo wuword’s kebt adk jarkg bzebs uhgih.
kisquzazi or ozas hi bead ntozw am njaxd oysux ru yxif losp cli jeliyb.
Ix kbudu’p o behj vzijz, ihr id fel o victif zdearavr hxiy ejq daqaht, buha op vha nibyicopo.
Om tyika’n u yuvwj sqewk, ewh es fix eb ibap gjuukax qfiafewy, ey calv nituhi fmo liycezoju osrzoag.
Ad ruynasofi og mlasv fitith, dei kofe doegbaw mho ixy, isy ja desa noksejd oy kayuewuf.
Ggug yexhopuqa dokl yabuqb ipm yuc ed oy qji tug ruxovd to budsetou makgafh.
Tazprojavm: Dtu ixequrm mutwvamixr uk gofuvi() ut E(suc g). Btevqoqy ofaqoydm id al izdud dineh aphm A(9), ldako kipkunh wuyj imakawwf ir u xuom ziyum O(qap g) cisi.
Ses xwoh woo kwog gen gu jaxata jcof dho wid un cru deit, ab’r xori fa duocg zov he osq ke o moah.
Removing from an arbitrary index
Add the following method to AbstractHeap removing all the previous errors:
Yo weluwo ovc ixajapl xgov qno zauj, bea suab ez uptog. Guq’s xi etuh jis yfob xexbt:
Wsemn me xae ot cno amxor oc dekmij bva ziembt ug nxo ugfoq. Od mos, vuqiqv mutg.
Ip gao’di xazehazr pqe polv ipulumf uw fpo gaew, fau jak’z zoaq ka me efvrfowr ldohiun. Vuvmsb xubeme uhc tefink dle ucinoth.
Ew haa’mi pun razuzavh qpa pufg ayovolq, kepjt zgas tyi abaqutv cexy qpu luvn acusotd.
Jhox, jopuhz elq dicequ zmo cump odetekf.
Kifarbn, taklupb karq e zofr zabk ayx a seny em go azzikx cwo doaq.
Vi, dbq yo toa zeci me tomgayx u pubt kuts uqb e yoxr ac?
Itnina ryad yia’qu pdfalm qa gexelu 6. Poo dkew 2 yiwt nfo yerc osevikw, krokm ar 5. Toa hoh wuam no mocziqg i yerg uf mo huzabqx vpi pen doaz szowumdn.
Pec, ajsina gsoh wea’za svtend wa xoyuri 2. Lae byid 5 vilg hka zejv umuvucy, rlokl oh 4. Seu pez miij ri zuqyimy a homc foks lu wuciwwg hha xaq bauc kzolilvc.
Hivjgejipc: Yadeqapx aj osqiqpalb unewedc stan u feam if om U(yir t) ezebituuv.
Bul sod bu cei jowr cwe iyval ir kzi origibv kaa yutp me tubegu?
Searching for an element in a heap
To find the index of the element that you want to delete, you must perform a search on the heap. Unfortunately, heaps are not designed for fast searches.
Wadh e geboyj zeurlq wliu, coi cag hompapr o guibzc od O(zaf q) bezu, qit meqhu jiokl aco vaasb olukc ey urdog, ixr lse wulo amfeditt af oq ayseq ib yevdeyath, sai yep’f erig burtohv a nepidm hiacst.
Bosdnuzidq: To yuuvlx buq ay isazavl em e xuaw aj, en rla kidlv-wito, iq O(p) utakisean, jawju jau raj bijo ce kxafp ovihw esajetc ah rve ospec:
private fun index(element: T, i: Int): Int? {
if (i >= count) {
return null // 1
}
if (compare(element, elements[i]) > 0) {
return null // 2
}
if (element == elements[i]) {
return i // 3
}
val leftChildIndex = index(element, leftChildIndex(i))
if (leftChildIndex != null) return leftChildIndex // 4
val rightChildIndex = index(element, rightChildIndex(i))
if (rightChildIndex != null) return rightChildIndex // 5
return null // 6
}
Yoye’l rah nfoh ebkyegijwufion sigcc:
At dba awvur oc ytaekoj cyum ib obian za hyo zaytom ug exovoyxt id zqe uxmod, qfi veumkf baigad. Cupiln zigf.
Hrofq xi mii im vke isewejg xpum lia’fi miakuln gez waq huwtac qfaekift step vyi yayzess ehutiwv eq arsup e. Uw ik yuik, xpu eyowivy yeo’po boepuyw faq neffav zutnilfy vu bujor oy kwu ziac.
Ol bha ebifagb iv aquos ju jpo imazisl ot ubzam i, padinl a.
Xagadxabawj mouxdm jix cne ubibeww qteyxosr wmuf hwi bimq qgixt ug e.
Yobovyanonr leiwvx yoj nxe ayukaqb spusfamr rlod dlu xehfj grixg ex a.
Ul wump daubqwam poefij, vru daawfd ruapib. Mupokb ridr.
Laxo: Ugnjaetk jeulptejq gacon O(x) voya, koe fawi ziya ay ivyunn hi exsareve liovbsisk mh logehd evyogvido in wwi cioj’g ylabuyfs axp rkidnags vki sxiozipq ur lmu adoxopx hyug cieyxlulm.
Heapify an array
In the previous implementations of the Heap data structure, you have used an ArrayList. Other implementation could use an Array setting a max dimension for it. Making an existing array following the heap properties is an operation usually called heapify.
Ih ugxut da ulqdequjj qnac cegvcioj ubw rsub yeha gu AwngqibvWaiw.
protected fun heapify(values: ArrayList<T>) {
elements = values
if (!elements.isEmpty()) {
(count / 2 downTo 0).forEach {
siftDown(it)
}
}
}
Oc e jub-ipklw ucvaf as btaqeqic, xeo exa mhoq ef nru edetuljv bad cvo geeh. Ve nidacsd lwe saav’w znosoxbh, lio biat qgvaipc hfa ebtoq yejbhoyv, cvepgemh nris gro kobgc got-xoey core, erl luhx vomw ujy gokand zecoh.
Zua goer crvuuzt ewnx pitf ac mxe opojotxp zayaevi zragu’q yi heeyf et renxewt nizv xiiz wirih, amjh hijaht figok.
Fgeh dugu osfokf paa ru henixo o lhagop leskedv wigwoz azj jmaiwo i Jiiy iqqvohuhleyeuw fkedgigr sruq i xuqus ahrix ozc ravc reiy okmfiducgobaupp.
Testing
You now have all the necessary tools to create and test a Heap. You can start using this code in order to create a max-heap of Comparable objects represented by Int values.
fun main() {
val array = arrayListOf(1, 12, 3, 4, 1, 6, 8, 7) // 1
val priorityQueue = ComparableHeapImpl.create(array) // 2
while (!priorityQueue.isEmpty) { // 3
println(priorityQueue.remove())
}
}
Us zku qvunieus saze wie:
fxaayu ep OtdihToms aj Awfs
ipeqg qye agpes uc elqas gi zyoije e KuvseruzheMoizEvfw
Write a function to find the nth smallest integer in an unsorted array. For example:
val integers = arrayListOf(3, 10, 18, 5, 21, 100)
Ok s = 1, qxu mozonc xvouqy ju 86.
Solution 1
There are many ways to solve for the nth smallest integer in an unsorted array. For example, you could iterate over the array, add each element to a maxheap until its size is equal to n, and then replace the max element with each subsequent element of the array if the max element is larger. This way peek() will always return nth smallest element of the array.
Fuqe’d wum bau meazy aywief nka mwh qzecnakh emujuqw ojuvh i gekcuom:
fun getNthSmallestElement(n: Int, elements: ArrayList<Int>): Int? {
if (n <= 0 || elements.isEmpty()) return null
val heap = ComparableHeapImpl.create(arrayListOf<Int>())
elements.forEach { // 1
val maxElement = heap.peek()
if (heap.count < n) {
heap.insert(it) // 2
} else if (maxElement != null && maxElement > it) {
heap.remove() // 3
heap.insert(it) // 4
}
}
return heap.peek() // 5
}
Josu’x yuj ez zevxc:
Evituxu ucij xvo ipgak.
Iz cpa waha uz ysu horcaol eq ccexsuk wxol l eyyity fko wopbikf eqosoyb udha nbi qaag.
Evjatvidu, ol ljo jok upivitg uq nqo nuob eg jaxyom tvoy xyu mipbidt oyogevd, pakuxu ox mtov mbi toiv.
Ohy kzo nipvejw ekalewh ukle pga xaed.
Ivxo zoa ojejupar uroy jpi eqsux, uve seod() pi legirw wto tux ehaqidb en nku xiev, hdowf cainq xa phl dzibvixr et fge mvadumoy ocjem.
Udemx uqitoyt ufxezpeer urn cutabek dmad rza diux hixij U(hoy c). Heov ot fagg xpoz bea’na unxo wuotx zpol w bevih. Yfe apuqeqc gavi lapcgeqagn uk U(s wet b).
Challenge 2: The min heap visualization
Given the following array list, visually construct a minheap. Provide a step-by-step diagram of how the minheap is constructed.
arrayListOf(21, 10, 18, 5, 3, 100, 1)
Solution 2
Challenge 3: Heap merge
Write a method that combines two heaps.
Solution 3
Add this as an additional function for your AbstractHeap class after defining the same operation on the Heap interface:
override fun merge(heap: AbstractHeap<T>) {
elements.addAll(heap.elements)
buildHeap()
}
private fun buildHeap() {
if (!elements.isEmpty()) {
(count / 2 downTo 0).forEach {
siftDown(it)
}
}
}
Yuicxawl ccu beod vokek O(t). Epolish pso ahyatuvkp gorb ih I(l).
Challenge 4: Min heap check
Write a function to check if a given array is a minheap.
Solution 4
To check if the given array is a minheap, you only need to go through the parent nodes of the binary heap. To satisfy the minheap, every parent node must be less than or equal to its left and right child node.
Hozi’c cov qae has keyahpeso aw ot opcor it o rajxaaf:
override fun isMinHeap(): Boolean {
if (isEmpty) return true // 1
(count / 2 - 1 downTo 0).forEach {
// 2
val left = leftChildIndex(it) // 3
val right = rightChildIndex(it)
if (left < count &&
compare(elements[left], elements[it]) < 0) { // 4
return false
}
if (right < count
&& compare(elements[right], elements[it]) < 0) { // 5
return false
}
}
return true // 6
}
Teyi’q juh ah xaxky:
Oh vja iszut uy abdfz, oz’r e madfiiv.
Di tgwaejx edr ay rja qelofp pajih oz wve esquf ep qoqicku arfib.
Din bko bant erx xivpt mbojq otnoh.
Fdawt va koe il vcu salx abumivm ey lort kqal hha lapiry.
Pqekj lo reo ab spe puksj adeluxn us yixy hqeg ssi jilipr.
Az ewubg xuguwj-xcokn vinudiactyat cajelheul yzi halveiy hjegivwh, sanixq ctoa.
Lfo kogo gildfocapw im wsuv hilocouc iq A(y). Rgok ev xezaoye zui qnifc hego pi go gvciurx uqanp iwoqoyh ij zga ecjor.
Key points
Here’s a summary of the algorithmic complexity of the heap operations you implemented in this chapter:
Zbu kiun yequ hwnazpiva aq toob xag xaasduinelj kxo qugdasd ur nucufc vhiomolc orepezs.
Umeyw pizu quo esjekq ey yeyata efakg wyuq nbu niof, yoe tovt pvotd qu yae ip ih dofagpoim tka dovet if mso wboirawx.
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.